[efi] Add EFI image format and basic runtime environment
authorMichael Brown <mcb30@etherboot.org>
Sun, 12 Oct 2008 00:55:55 +0000 (01:55 +0100)
committerMichael Brown <mcb30@etherboot.org>
Mon, 13 Oct 2008 09:24:14 +0000 (10:24 +0100)
We have EFI APIs for CPU I/O, PCI I/O, timers, console I/O, user
access and user memory allocation.

EFI executables are created using the vanilla GNU toolchain, with the
EXE header handcrafted in assembly and relocations generated by a
custom efilink utility.

40 files changed:
src/Makefile
src/Makefile.housekeeping
src/arch/i386/Makefile
src/arch/i386/Makefile.efi [new file with mode: 0644]
src/arch/i386/include/bits/nap.h
src/arch/i386/include/gpxe/efi/efix86_nap.h [new file with mode: 0644]
src/arch/i386/interface/efi/efix86_nap.c [new file with mode: 0644]
src/arch/i386/prefix/efiprefix.S [new file with mode: 0644]
src/arch/i386/scripts/efi.lds [new file with mode: 0644]
src/config/defaults/efi.h [new file with mode: 0644]
src/config/general.h
src/core/config.c
src/image/efi_image.c [new file with mode: 0644]
src/include/gpxe/efi/IndustryStandard/PeImage.h [new file with mode: 0644]
src/include/gpxe/efi/Protocol/Cpu.h [new file with mode: 0644]
src/include/gpxe/efi/Protocol/CpuIo.h [new file with mode: 0644]
src/include/gpxe/efi/Protocol/DebugSupport.h [new file with mode: 0644]
src/include/gpxe/efi/Protocol/PciRootBridgeIo.h [new file with mode: 0644]
src/include/gpxe/efi/efi.h
src/include/gpxe/efi/efi_io.h [new file with mode: 0644]
src/include/gpxe/efi/efi_pci.h [new file with mode: 0644]
src/include/gpxe/efi/efi_timer.h [new file with mode: 0644]
src/include/gpxe/efi/efi_uaccess.h [new file with mode: 0644]
src/include/gpxe/efi/efi_umalloc.h [new file with mode: 0644]
src/include/gpxe/errfile.h
src/include/gpxe/features.h
src/include/gpxe/io.h
src/include/gpxe/pci_io.h
src/include/gpxe/timer.h
src/include/gpxe/uaccess.h
src/include/gpxe/umalloc.h
src/interface/efi/efi_console.c [new file with mode: 0644]
src/interface/efi/efi_entry.c [new file with mode: 0644]
src/interface/efi/efi_io.c [new file with mode: 0644]
src/interface/efi/efi_pci.c [new file with mode: 0644]
src/interface/efi/efi_timer.c [new file with mode: 0644]
src/interface/efi/efi_uaccess.c [new file with mode: 0644]
src/interface/efi/efi_umalloc.c [new file with mode: 0644]
src/util/.gitignore
src/util/efilink.c [new file with mode: 0644]

index 89f4357..01048aa 100644 (file)
@@ -37,6 +37,7 @@ SYMCHECK      := $(PERL) ./util/symcheck.pl
 SORTOBJDUMP    := $(PERL) ./util/sortobjdump.pl
 NRV2B          := ./util/nrv2b
 ZBIN           := ./util/zbin
+EFILINK                := ./util/efilink
 DOXYGEN                := doxygen
 
 ###############################################################################
@@ -57,7 +58,7 @@ SRCDIRS               += drivers/block
 SRCDIRS                += drivers/nvs
 SRCDIRS                += drivers/bitbash
 SRCDIRS                += drivers/infiniband
-SRCDIRS                += interface/pxe
+SRCDIRS                += interface/pxe interface/efi
 SRCDIRS                += tests
 SRCDIRS                += crypto crypto/axtls crypto/matrixssl
 SRCDIRS                += hci hci/commands hci/tui
index 14d07fb..7054cdc 100644 (file)
@@ -693,6 +693,15 @@ $(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS)
        $(Q)$(HOST_CC) -O2 -o $@ $<
 CLEANUP += $(ZBIN)
 
+###############################################################################
+#
+# The EFI custom linker
+#
+$(EFILINK) : util/efilink.c $(MAKEDEPS)
+       $(QM)$(ECHO) "  [HOSTCC] $@"
+       $(Q)$(HOST_CC) -O2 -o $@ $< -lbfd
+CLEANUP += $(EFILINK)
+
 ###############################################################################
 #
 # Auto-incrementing build serial number.  Append "bs" to your list of
index 1959fbf..dac5349 100644 (file)
@@ -64,6 +64,7 @@ SRCDIRS               += arch/i386/drivers/net
 SRCDIRS                += arch/i386/interface/pcbios
 SRCDIRS                += arch/i386/interface/pxe
 SRCDIRS        += arch/i386/interface/syslinux
+SRCDIRS        += arch/i386/interface/efi
 
 # The various xxx_loader.c files are #included into core/loader.c and
 # should not be compiled directly.
diff --git a/src/arch/i386/Makefile.efi b/src/arch/i386/Makefile.efi
new file mode 100644 (file)
index 0000000..f1eb6fd
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# The EFI linker script
+#
+LDSCRIPT       = arch/i386/scripts/efi.lds
+
+# Use a relocatable link; we perform final relocations in the efilink utility.
+#
+LDFLAGS                += -r -d -S
+
+# Media types.
+#
+NON_AUTO_MEDIA += efi
+
+# Rule for building EFI files
+#
+$(BIN)/%.efi.tmp-reloc : $(BIN)/%.efi.tmp $(EFILINK)
+       $(QM)$(ECHO) "  [EFILINK] $@"
+       $(Q)$(LD) -e 0 -o /dev/null $< # Check for unresolved symbols
+       $(Q)$(EFILINK) $< $@
+
+$(BIN)/%.efi : $(BIN)/%.efi.tmp-reloc
+       $(QM)$(ECHO) "  [FINISH] $@"
+       $(Q)$(OBJCOPY) -Obinary $< $@
index 2c85444..f8ba7a7 100644 (file)
@@ -8,5 +8,6 @@
  */
 
 #include <gpxe/bios_nap.h>
+#include <gpxe/efi/efix86_nap.h>
 
 #endif /* _BITS_MAP_H */
diff --git a/src/arch/i386/include/gpxe/efi/efix86_nap.h b/src/arch/i386/include/gpxe/efi/efix86_nap.h
new file mode 100644 (file)
index 0000000..91424c5
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _GPXE_EFIX86_NAP_H
+#define _GPXE_EFIX86_NAP_H
+
+/** @file
+ *
+ * EFI CPU sleeping
+ *
+ */
+
+#ifdef NAP_EFIX86
+#define NAP_PREFIX_efix86
+#else
+#define NAP_PREFIX_efix86 __efix86_
+#endif
+
+#endif /* _GPXE_EFIX86_NAP_H */
diff --git a/src/arch/i386/interface/efi/efix86_nap.c b/src/arch/i386/interface/efi/efix86_nap.c
new file mode 100644 (file)
index 0000000..45e99a6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/nap.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE CPU sleeping API for EFI
+ *
+ */
+
+/**
+ * Sleep until next interrupt
+ *
+ */
+static void efix86_cpu_nap ( void ) {
+       /*
+        * I can't find any EFI API that allows us to put the CPU to
+        * sleep.  The CpuSleep() function is defined in CpuLib.h, but
+        * isn't part of any exposed protocol so we have no way to
+        * call it.
+        *
+        * The EFI shell doesn't seem to bother sleeping the CPU; it
+        * just sits there idly burning power.
+        *
+        */
+       __asm__ __volatile__ ( "hlt" );
+}
+
+PROVIDE_NAP ( efix86, cpu_nap, efix86_cpu_nap );
diff --git a/src/arch/i386/prefix/efiprefix.S b/src/arch/i386/prefix/efiprefix.S
new file mode 100644 (file)
index 0000000..c4cf68e
--- /dev/null
@@ -0,0 +1,175 @@
+       .text
+       .code32
+       .arch i386
+       .section ".prefix", "a", @progbits
+       .org 0x00
+
+       /* DOS (.com) header
+        *
+        * EFI executables seem to leave most of this empty
+        */
+mzhdr:
+       .ascii  "MZ"            /* Magic number */
+       .word   0               /* Bytes on last page of file */
+       .word   0               /* Pages in file */
+       .word   0               /* Relocations */
+       .word   0               /* Size of header in paragraphs */
+       .word   0               /* Minimum extra paragraphs needed */
+       .word   0               /* Maximum extra paragraphs needed */
+       .word   0               /* Initial (relative) SS value */
+       .word   0               /* Initial SP value */
+       .word   0               /* "Checksum" */
+       .word   0               /* Initial IP value */
+       .word   0               /* Initial (relative) CS value */
+       .word   0               /* File address of relocation table */
+       .word   0               /* Ovesrlay number */
+       .word   0, 0, 0, 0      /* Reserved words */
+       .word   0               /* OEM identifier (for e_oeminfo) */
+       .word   0               /* OEM information; e_oemid specific */
+       .word   0, 0, 0, 0, 0   /* Reserved words */
+       .word   0, 0, 0, 0, 0   /* Reserved words */
+       .long   pehdr_lma       /* File address of new exe header */
+       .size   mzhdr, . - mzhdr
+
+       /* PE header */
+       .org    0xc0 /* For compatibility with MS toolchain */
+pehdr:
+       .ascii  "PE\0\0"        /* Magic number */
+       .word   0x014c          /* CPU architecture: i386 */
+       .word   num_pe_sections /* Number of sections */
+       .long   0x10d1a884      /* Timestamp */
+       .long   0               /* Symbol table */
+       .long   0               /* Number of symbols */
+       .word   opthdr_size     /* Size of optional header */
+       .word   0x2102          /* Characteristics */
+       .size   pehdr, . - pehdr
+       .equ    pehdr_lma, pehdr - mzhdr
+
+       /* "Optional" header */
+opthdr:
+       .word   0x010b          /* Magic number */
+       .byte   0               /* Linker major version number */
+       .byte   0               /* Linker minor version number */
+       .long   _text_filesz    /* Size of text section */
+       .long   _data_filesz    /* Size of data section */
+       .long   _bss_filesz     /* Size of bss section */
+       .long   efi_entry_lma   /* Entry point */
+       .long   _text_lma       /* Text section start RVA */
+       .long   _data_lma       /* Data section start RVA */
+       .long   0               /* Image base address */
+       .long   _max_align      /* Section alignment */
+       .long   _max_align      /* File alignment */
+       .word   0               /* Operating system major version number */
+       .word   0               /* Operating system minor version number */
+       .word   0               /* Image major version number */
+       .word   0               /* Image minor version number */
+       .word   0               /* Subsystem major version number */
+       .word   0               /* Subsystem minor version number */
+       .long   0               /* Reserved */
+       .long   _filesz         /* Total image size */
+       .long   _prefix_filesz  /* Total header size */
+       .long   0               /* "Checksum" */
+       .word   0x0a            /* Subsystem: EFI */
+       .word   0               /* DLL characteristics */
+       .long   0               /* Size of stack reserve */
+       .long   0               /* Size of stack commit */
+       .long   0               /* Size of heap reserve */
+       .long   0               /* Size of heap commit */
+       .long   0               /* Loader flags */
+       .long   16              /* Number of data directory entries */
+       .long   0, 0            /* Export directory */
+       .long   0, 0            /* Import directory */
+       .long   0, 0            /* Resource directory */
+       .long   0, 0            /* Exception directory */
+       .long   0, 0            /* Security directory */
+       .long   _reloc_lma, _reloc_filesz /* Base relocation directory */
+       .long   debugdir_lma, debugdir_size /* Debug directory */
+       .long   0, 0            /* Description directory */
+       .long   0, 0            /* Special directory */
+       .long   0, 0            /* Thread storage directory */
+       .long   0, 0            /* Load configuration directory */
+       .long   0, 0            /* Bound import directory */
+       .long   0, 0            /* Import address table directory */
+       .long   0, 0            /* Delay import directory */
+       .long   0, 0            /* Reserved */
+       .long   0, 0            /* Reserved */
+       .size   opthdr, . - opthdr
+       .equ    opthdr_size, . - opthdr
+
+       /* PE sections */
+pe_sections:
+text_section:
+       .asciz  ".text"         /* Section name */
+       .align  8
+       .long   _text_filesz    /* Section size */
+       .long   _text_lma       /* Relative Virtual Address */
+       .long   _text_filesz    /* Section size (rounded up) */
+       .long   _text_lma       /* Pointer to raw data */
+       .long   0               /* Link-time relocations */
+       .long   0               /* Line numbers */
+       .word   0               /* Number of link-time relocations */
+       .word   0               /* Number of line numbers */
+       .long   0x68000020      /* Characteristics */
+rodata_section:
+       .asciz  ".rodata"       /* Section name */
+       .align  8
+       .long   _rodata_filesz  /* Section size */
+       .long   _rodata_lma     /* Relative Virtual Address */
+       .long   _rodata_filesz  /* Section size (rounded up) */
+       .long   _rodata_lma     /* Pointer to raw data */
+       .long   0               /* Link-time relocations */
+       .long   0               /* Line numbers */
+       .word   0               /* Number of link-time relocations */
+       .word   0               /* Number of line numbers */
+       .long   0x48000040      /* Characteristics */
+data_section:
+       .asciz  ".data"         /* Section name */
+       .align  8
+       .long   _data_filesz    /* Section size */
+       .long   _data_lma       /* Relative Virtual Address */
+       .long   _data_filesz    /* Section size (rounded up) */
+       .long   _data_lma       /* Pointer to raw data */
+       .long   0               /* Link-time relocations */
+       .long   0               /* Line numbers */
+       .word   0               /* Number of link-time relocations */
+       .word   0               /* Number of line numbers */
+       .long   0xc8000040      /* Characteristics */
+reloc_section:
+       .asciz  ".reloc"        /* Section name */
+       .align  8
+       .long   _reloc_filesz   /* Section size */
+       .long   _reloc_lma      /* Relative Virtual Address */
+       .long   _reloc_filesz   /* Section size (rounded up) */
+       .long   _reloc_lma      /* Pointer to raw data */
+       .long   0               /* Link-time relocations */
+       .long   0               /* Line numbers */
+       .word   0               /* Number of link-time relocations */
+       .word   0               /* Number of line numbers */
+       .long   0x42000040      /* Characteristics */
+
+pe_sections_end:
+       .size   pe_sections, . - pe_sections
+       .equ    num_pe_sections, ( ( . - pe_sections ) / 0x28 )
+
+       /* Debug directory */
+       .section ".rodata"
+       .globl  debugdir
+debugdir:
+       .long   0               /* Characteristics */
+       .long   0x10d1a884      /* Timestamp */
+       .word   0               /* Major version */
+       .word   0               /* Minor version */
+       .long   0x02            /* RSDS? */
+       .long   codeview_rsds_size /* Size of data */
+       .long   codeview_rsds_lma /* RVA */
+       .long   codeview_rsds_lma /* File offset */
+       .size   debugdir, . - debugdir
+       .equ    debugdir_size, . - debugdir
+       /* Codeview structure */
+       .globl  codeview_rsds
+codeview_rsds:
+       .ascii  "RSDS"          /* Magic number */
+       .long   0, 0, 0, 0, 0   /* Unused by EFI */
+       .asciz  "efiprefix.pdb"
+       .size   codeview_rsds, . - codeview_rsds
+       .equ    codeview_rsds_size, . - codeview_rsds
diff --git a/src/arch/i386/scripts/efi.lds b/src/arch/i386/scripts/efi.lds
new file mode 100644 (file)
index 0000000..8d9ecd7
--- /dev/null
@@ -0,0 +1,174 @@
+/* -*- sh -*- */
+
+/*
+ * Linker script for EFI images
+ *
+ */
+
+EXTERN ( efi_entry )
+
+SECTIONS {
+
+    /* The file starts at a virtual address of zero, and sections are
+     * contiguous.  Each section is aligned to at least _max_align,
+     * which defaults to 32.  Load addresses are equal to virtual
+     * addresses.
+     */
+
+    . = 0;
+    PROVIDE ( _max_align = 32 );
+
+    /*
+     * The prefix
+     *
+     */
+
+    .prefix : AT ( _prefix_lma ) {
+       _prefix = .;
+       *(.prefix)
+       *(.prefix.*)
+       _mprefix = .;
+    } .prefix_bss (NOLOAD) : {
+       _eprefix = .;
+    }
+    _prefix_filesz     = ABSOLUTE ( _mprefix - _prefix );
+    _prefix_memsz      = ABSOLUTE ( _eprefix - _prefix );
+
+    /*
+     * The text section
+     *
+     */
+
+    . = ALIGN ( _max_align );
+    .text : AT ( _text_lma ) {
+       _text = .;
+       *(.text)
+       *(.text.*)
+       _mtext = .;
+    } .text_bss (NOLOAD) : {
+       _etext = .;
+    }
+    _text_filesz       = ABSOLUTE ( _mtext - _text );
+    _text_memsz                = ABSOLUTE ( _etext - _text );
+
+    /*
+     * The rodata section
+     *
+     */
+
+    . = ALIGN ( _max_align );
+    .rodata : AT ( _rodata_lma ) {
+       _rodata = .;
+       *(.rodata)
+       *(.rodata.*)
+       _mrodata = .;
+    } .rodata_bss (NOLOAD) : {
+       _erodata = .;
+    }
+    _rodata_filesz     = ABSOLUTE ( _mrodata - _rodata );
+    _rodata_memsz      = ABSOLUTE ( _erodata - _rodata );
+
+    /*
+     * The data section
+     *
+     */
+
+    . = ALIGN ( _max_align );
+    .data : AT ( _data_lma ) {
+       _data = .;
+       *(.data)
+       *(.data.*)
+       *(SORT(.tbl.*))         /* Various tables.  See include/tables.h */
+       /* EFI seems to not support proper bss sections */
+       *(.bss)
+       *(.bss.*)
+       *(COMMON)
+       *(.stack)
+       *(.stack.*)
+       _mdata = .;
+    } .data_bss (NOLOAD) : {
+       _edata = .;
+    }
+    _data_filesz       = ABSOLUTE ( _mdata - _data );
+    _data_memsz                = ABSOLUTE ( _edata - _data );
+
+    /*
+     * The bss section
+     *
+     */
+
+    . = ALIGN ( _max_align );
+    .bss : AT ( _bss_lma ) {
+       _bss = .;
+       /* EFI seems to not support proper bss sections */
+       _mbss = .;
+    } .bss_bss (NOLOAD) : {
+       _ebss = .;
+    }
+    _bss_filesz                = ABSOLUTE ( _mbss - _bss );
+    _bss_memsz         = ABSOLUTE ( _ebss - _bss );
+
+    /*
+     * The reloc section
+     *
+     */
+
+    . = ALIGN ( _max_align );
+    .reloc : AT ( _reloc_lma ) {
+       _reloc = .;
+       /* Provide some dummy contents to force ld to include this
+        * section.  It will be created by the efilink utility.
+        */
+       . += 1;
+       _mreloc = .;
+    } .reloc_bss (NOLOAD) : {
+       _ereloc = .;
+    }
+    _reloc_filesz      = ABSOLUTE ( _mreloc - _reloc );
+    _reloc_memsz       = ABSOLUTE ( _ereloc - _reloc );
+
+    _filesz            = ABSOLUTE ( . );
+
+    /*
+     * Weak symbols that need zero values if not otherwise defined
+     *
+     */
+
+    .weak 0x0 : {
+       _weak = .;
+       *(.weak)
+       _eweak = .;
+    }
+    _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
+
+    /*
+     * Dispose of the comment and note sections to make the link map
+     * easier to read
+     *
+     */
+
+    /DISCARD/ : {
+       *(.comment)
+       *(.note)
+    }
+
+    /*
+     * Load address calculations.
+     *
+     */
+
+    _prefix_lma                = ABSOLUTE ( _prefix );
+    _text_lma          = ABSOLUTE ( _text );
+    _rodata_lma                = ABSOLUTE ( _rodata );
+    _data_lma          = ABSOLUTE ( _data );
+    _bss_lma           = ABSOLUTE ( _bss );
+    _reloc_lma         = ABSOLUTE ( _reloc );
+
+    /*
+     * Load addresses required by the prefix
+     *
+     */
+    efi_entry_lma      = ABSOLUTE ( efi_entry );
+    debugdir_lma       = ABSOLUTE ( debugdir );
+    codeview_rsds_lma  = ABSOLUTE ( codeview_rsds );
+}
diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h
new file mode 100644 (file)
index 0000000..d980136
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef CONFIG_DEFAULTS_EFI_H
+#define CONFIG_DEFAULTS_EFI_H
+
+/** @file
+ *
+ * Configuration defaults for EFI
+ *
+ */
+
+#define UACCESS_EFI
+#define IOAPI_EFI
+#define PCIAPI_EFI
+#define CONSOLE_EFI
+#define TIMER_EFI
+#define NAP_EFIX86
+#define UMALLOC_EFI
+
+#define        IMAGE_EFI               /* EFI image support */
+
+#endif /* CONFIG_DEFAULTS_EFI_H */
index d18c9cc..3d9663b 100644 (file)
@@ -70,6 +70,7 @@
 //#define      IMAGE_SCRIPT            /* gPXE script image support */
 //#define      IMAGE_BZIMAGE           /* Linux bzImage image support */
 //#define      IMAGE_COMBOOT           /* SYSLINUX COMBOOT image support */
+//#define      IMAGE_EFI               /* EFI image support */
 
 /*
  * Command-line commands to include
index ee16b9c..b14d25a 100644 (file)
@@ -59,6 +59,9 @@ REQUIRE_OBJECT ( pc_kbd );
 #ifdef CONSOLE_SYSLOG
 REQUIRE_OBJECT ( syslog );
 #endif
+#ifdef CONSOLE_EFI
+REQUIRE_OBJECT ( efi_console );
+#endif
 
 /*
  * Drag in all requested network protocols
@@ -158,6 +161,9 @@ REQUIRE_OBJECT ( com32_call );
 REQUIRE_OBJECT ( com32_wrapper );
 REQUIRE_OBJECT ( comboot_resolv );
 #endif
+#ifdef IMAGE_EFI
+REQUIRE_OBJECT ( efi_image );
+#endif
 
 /*
  * Drag in all requested commands
diff --git a/src/image/efi_image.c b/src/image/efi_image.c
new file mode 100644 (file)
index 0000000..c80cd26
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/image.h>
+#include <gpxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
+
+struct image_type efi_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * Execute EFI image
+ *
+ * @v image            EFI image
+ * @ret rc             Return status code
+ */
+static int efi_image_exec ( struct image *image ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_HANDLE handle;
+       UINTN exit_data_size;
+       CHAR16 *exit_data;
+       EFI_STATUS efirc;
+
+       /* Attempt loading image */
+       if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL,
+                                      user_to_virt ( image->data, 0 ),
+                                      image->len, &handle ) ) != 0 ) {
+               /* Not an EFI image */
+               DBGC ( image, "EFIIMAGE %p could not load: %lx\n",
+                      image, efirc );
+               return -ENOEXEC;
+       }
+
+       /* Start the image */
+       if ( ( efirc = bs->StartImage ( handle, &exit_data_size,
+                                       &exit_data ) ) != 0 ) {
+               DBGC ( image, "EFIIMAGE %p returned with status %lx\n",
+                      image, efirc );
+               goto done;
+       }
+
+ done:
+       /* Unload the image.  We can't leave it loaded, because we
+        * have no "unload" operation.
+        */
+       bs->UnloadImage ( handle );
+
+       return EFIRC_TO_RC ( efirc );
+}
+
+/**
+ * Load EFI image into memory
+ *
+ * @v image            EFI file
+ * @ret rc             Return status code
+ */
+static int efi_image_load ( struct image *image ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_HANDLE handle;
+       EFI_STATUS efirc;
+
+       /* Attempt loading image */
+       if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL,
+                                      user_to_virt ( image->data, 0 ),
+                                      image->len, &handle ) ) != 0 ) {
+               /* Not an EFI image */
+               DBGC ( image, "EFIIMAGE %p could not load: %lx\n",
+                      image, efirc );
+               return -ENOEXEC;
+       }
+
+       /* This is an EFI image */
+       if ( ! image->type )
+               image->type = &efi_image_type;
+
+       /* Unload the image.  We can't leave it loaded, because we
+        * have no "unload" operation.
+        */
+       bs->UnloadImage ( handle );
+
+       return 0;
+}
+
+/** EFI image type */
+struct image_type efi_image_type __image_type ( PROBE_NORMAL ) = {
+       .name = "EFI",
+       .load = efi_image_load,
+       .exec = efi_image_exec,
+};
diff --git a/src/include/gpxe/efi/IndustryStandard/PeImage.h b/src/include/gpxe/efi/IndustryStandard/PeImage.h
new file mode 100644 (file)
index 0000000..666d4ec
--- /dev/null
@@ -0,0 +1,742 @@
+/** @file
+  EFI image format for PE32 and PE32+. Please note some data structures are
+  different for PE32 and PE32+. EFI_IMAGE_NT_HEADERS32 is for PE32 and
+  EFI_IMAGE_NT_HEADERS64 is for PE32+.
+
+  This file is coded to the Visual Studio, Microsoft Portable Executable and
+  Common Object File Format Specification, Revision 8.0 - May 16, 2006.
+
+  Copyright (c) 2006 - 2007, Intel Corporation
+  All rights reserved. This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IMAGE_H__
+#define __EFI_IMAGE_H__
+
+//
+// PE32+ Subsystem type for EFI images
+//
+#define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION         10
+#define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
+#define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER      12
+#define EFI_IMAGE_SUBSYSTEM_EFI_EFI_ROM             13
+
+#define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER      13
+
+
+//
+// PE32+ Machine type for EFI images
+//
+#define IMAGE_FILE_MACHINE_I386     0x014c
+#define IMAGE_FILE_MACHINE_IA64     0x0200
+#define IMAGE_FILE_MACHINE_EBC      0x0EBC
+#define IMAGE_FILE_MACHINE_X64      0x8664
+//
+// Support old names for backward compatible
+//
+#define EFI_IMAGE_MACHINE_IA32      IMAGE_FILE_MACHINE_I386
+#define EFI_IMAGE_MACHINE_IA64      IMAGE_FILE_MACHINE_IA64
+#define EFI_IMAGE_MACHINE_IPF       IMAGE_FILE_MACHINE_IA64
+#define EFI_IMAGE_MACHINE_EBC       IMAGE_FILE_MACHINE_EBC
+#define EFI_IMAGE_MACHINE_X64       IMAGE_FILE_MACHINE_X64
+
+#define EFI_IMAGE_DOS_SIGNATURE     0x5A4D      // MZ
+#define EFI_IMAGE_OS2_SIGNATURE     0x454E      // NE
+#define EFI_IMAGE_OS2_SIGNATURE_LE  0x454C      // LE
+#define EFI_IMAGE_NT_SIGNATURE      0x00004550  // PE00
+
+///
+/// PE images can start with an optional DOS header, so if an image is run
+///  under DOS it can print an error message.
+///
+typedef struct {
+  UINT16  e_magic;    // Magic number
+  UINT16  e_cblp;     // Bytes on last page of file
+  UINT16  e_cp;       // Pages in file
+  UINT16  e_crlc;     // Relocations
+  UINT16  e_cparhdr;  // Size of header in paragraphs
+  UINT16  e_minalloc; // Minimum extra paragraphs needed
+  UINT16  e_maxalloc; // Maximum extra paragraphs needed
+  UINT16  e_ss;       // Initial (relative) SS value
+  UINT16  e_sp;       // Initial SP value
+  UINT16  e_csum;     // Checksum
+  UINT16  e_ip;       // Initial IP value
+  UINT16  e_cs;       // Initial (relative) CS value
+  UINT16  e_lfarlc;   // File address of relocation table
+  UINT16  e_ovno;     // Overlay number
+  UINT16  e_res[4];   // Reserved words
+  UINT16  e_oemid;    // OEM identifier (for e_oeminfo)
+  UINT16  e_oeminfo;  // OEM information; e_oemid specific
+  UINT16  e_res2[10]; // Reserved words
+  UINT32  e_lfanew;   // File address of new exe header
+} EFI_IMAGE_DOS_HEADER;
+
+///
+/// File header format.
+///
+typedef struct {
+  UINT16  Machine;
+  UINT16  NumberOfSections;
+  UINT32  TimeDateStamp;
+  UINT32  PointerToSymbolTable;
+  UINT32  NumberOfSymbols;
+  UINT16  SizeOfOptionalHeader;
+  UINT16  Characteristics;
+} EFI_IMAGE_FILE_HEADER;
+
+#define EFI_IMAGE_SIZEOF_FILE_HEADER        20
+
+#define EFI_IMAGE_FILE_RELOCS_STRIPPED      0x0001  // Relocation info stripped from file.
+#define EFI_IMAGE_FILE_EXECUTABLE_IMAGE     0x0002  // File is executable  (i.e. no unresolved externel references).
+#define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED   0x0004  // Line nunbers stripped from file.
+#define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED  0x0008  // Local symbols stripped from file.
+#define EFI_IMAGE_FILE_BYTES_REVERSED_LO    0x0080  // Bytes of machine word are reversed.
+#define EFI_IMAGE_FILE_32BIT_MACHINE        0x0100  // 32 bit word machine.
+#define EFI_IMAGE_FILE_DEBUG_STRIPPED       0x0200  // Debugging info stripped from file in .DBG file
+#define EFI_IMAGE_FILE_SYSTEM               0x1000  // System File.
+#define EFI_IMAGE_FILE_DLL                  0x2000  // File is a DLL.
+#define EFI_IMAGE_FILE_BYTES_REVERSED_HI    0x8000  // Bytes of machine word are reversed.
+#define EFI_IMAGE_FILE_MACHINE_UNKNOWN      0
+#define EFI_IMAGE_FILE_MACHINE_I386         0x14c   // Intel 386.
+#define EFI_IMAGE_FILE_MACHINE_R3000        0x162   // MIPS* little-endian, 0540 big-endian
+#define EFI_IMAGE_FILE_MACHINE_R4000        0x166   // MIPS* little-endian
+#define EFI_IMAGE_FILE_MACHINE_ALPHA        0x184   // Alpha_AXP*
+#define EFI_IMAGE_FILE_MACHINE_POWERPC      0x1F0   // IBM* PowerPC Little-Endian
+#define EFI_IMAGE_FILE_MACHINE_TAHOE        0x7cc   // Intel EM machine
+//
+// * Other names and brands may be claimed as the property of others.
+//
+
+///
+/// Directory format.
+///
+typedef struct {
+  UINT32  VirtualAddress;
+  UINT32  Size;
+} EFI_IMAGE_DATA_DIRECTORY;
+
+#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
+
+typedef struct {
+  UINT16  Magic;
+  UINT8   MajorLinkerVersion;
+  UINT8   MinorLinkerVersion;
+  UINT32  SizeOfCode;
+  UINT32  SizeOfInitializedData;
+  UINT32  SizeOfUninitializedData;
+  UINT32  AddressOfEntryPoint;
+  UINT32  BaseOfCode;
+  UINT32  BaseOfData;
+  UINT32  BaseOfBss;
+  UINT32  GprMask;
+  UINT32  CprMask[4];
+  UINT32  GpValue;
+} EFI_IMAGE_ROM_OPTIONAL_HEADER;
+
+#define EFI_IMAGE_ROM_OPTIONAL_HDR_MAGIC      0x107
+#define EFI_IMAGE_SIZEOF_ROM_OPTIONAL_HEADER  sizeof (EFI_IMAGE_ROM_OPTIONAL_HEADER)
+
+typedef struct {
+  EFI_IMAGE_FILE_HEADER         FileHeader;
+  EFI_IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
+} EFI_IMAGE_ROM_HEADERS;
+
+///
+/// @attention
+/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and
+/// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary
+/// after NT additional fields.
+///
+#define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
+
+typedef struct {
+  ///
+  /// Standard fields.
+  ///
+  UINT16                    Magic;
+  UINT8                     MajorLinkerVersion;
+  UINT8                     MinorLinkerVersion;
+  UINT32                    SizeOfCode;
+  UINT32                    SizeOfInitializedData;
+  UINT32                    SizeOfUninitializedData;
+  UINT32                    AddressOfEntryPoint;
+  UINT32                    BaseOfCode;
+  UINT32                    BaseOfData;
+  ///
+  /// NT additional fields.
+  ///
+  UINT32                    ImageBase;
+  UINT32                    SectionAlignment;
+  UINT32                    FileAlignment;
+  UINT16                    MajorOperatingSystemVersion;
+  UINT16                    MinorOperatingSystemVersion;
+  UINT16                    MajorImageVersion;
+  UINT16                    MinorImageVersion;
+  UINT16                    MajorSubsystemVersion;
+  UINT16                    MinorSubsystemVersion;
+  UINT32                    Win32VersionValue;
+  UINT32                    SizeOfImage;
+  UINT32                    SizeOfHeaders;
+  UINT32                    CheckSum;
+  UINT16                    Subsystem;
+  UINT16                    DllCharacteristics;
+  UINT32                    SizeOfStackReserve;
+  UINT32                    SizeOfStackCommit;
+  UINT32                    SizeOfHeapReserve;
+  UINT32                    SizeOfHeapCommit;
+  UINT32                    LoaderFlags;
+  UINT32                    NumberOfRvaAndSizes;
+  EFI_IMAGE_DATA_DIRECTORY  DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
+} EFI_IMAGE_OPTIONAL_HEADER32;
+
+///
+/// @attention
+/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and
+/// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary
+/// after NT additional fields.
+///
+#define EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
+
+typedef struct {
+  //
+  // Standard fields.
+  //
+  UINT16                    Magic;
+  UINT8                     MajorLinkerVersion;
+  UINT8                     MinorLinkerVersion;
+  UINT32                    SizeOfCode;
+  UINT32                    SizeOfInitializedData;
+  UINT32                    SizeOfUninitializedData;
+  UINT32                    AddressOfEntryPoint;
+  UINT32                    BaseOfCode;
+  //
+  // NT additional fields.
+  //
+  UINT64                    ImageBase;
+  UINT32                    SectionAlignment;
+  UINT32                    FileAlignment;
+  UINT16                    MajorOperatingSystemVersion;
+  UINT16                    MinorOperatingSystemVersion;
+  UINT16                    MajorImageVersion;
+  UINT16                    MinorImageVersion;
+  UINT16                    MajorSubsystemVersion;
+  UINT16                    MinorSubsystemVersion;
+  UINT32                    Win32VersionValue;
+  UINT32                    SizeOfImage;
+  UINT32                    SizeOfHeaders;
+  UINT32                    CheckSum;
+  UINT16                    Subsystem;
+  UINT16                    DllCharacteristics;
+  UINT64                    SizeOfStackReserve;
+  UINT64                    SizeOfStackCommit;
+  UINT64                    SizeOfHeapReserve;
+  UINT64                    SizeOfHeapCommit;
+  UINT32                    LoaderFlags;
+  UINT32                    NumberOfRvaAndSizes;
+  EFI_IMAGE_DATA_DIRECTORY  DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
+} EFI_IMAGE_OPTIONAL_HEADER64;
+
+
+///
+/// @attention
+/// EFI_IMAGE_NT_HEADERS32 and EFI_IMAGE_HEADERS64 are for use ONLY
+/// by tools.  All proper EFI code MUST use EFI_IMAGE_NT_HEADERS ONLY!!!
+///
+typedef struct {
+  UINT32                      Signature;
+  EFI_IMAGE_FILE_HEADER       FileHeader;
+  EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader;
+} EFI_IMAGE_NT_HEADERS32;
+
+#define EFI_IMAGE_SIZEOF_NT_OPTIONAL32_HEADER sizeof (EFI_IMAGE_NT_HEADERS32)
+
+typedef struct {
+  UINT32                      Signature;
+  EFI_IMAGE_FILE_HEADER       FileHeader;
+  EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} EFI_IMAGE_NT_HEADERS64;
+
+#define EFI_IMAGE_SIZEOF_NT_OPTIONAL64_HEADER sizeof (EFI_IMAGE_NT_HEADERS64)
+
+
+//
+// Processor specific definition of EFI_IMAGE_OPTIONAL_HEADER so the
+// type name EFI_IMAGE_OPTIONAL_HEADER is appropriate to the build.  Same for
+// EFI_IMAGE_NT_HEADERS.  These definitions MUST be used by ALL EFI code.
+//
+#if   defined (MDE_CPU_IA32)
+
+#define EFI_IMAGE_MACHINE_TYPE_SUPPORTED(Machine) \
+  (((Machine) == EFI_IMAGE_MACHINE_IA32) || ((Machine) == EFI_IMAGE_MACHINE_EBC))
+
+#define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) ((Machine) == EFI_IMAGE_MACHINE_X64)
+
+//
+// @bug - Remove me when other package updated.
+//
+typedef EFI_IMAGE_NT_HEADERS32    EFI_IMAGE_NT_HEADERS;
+
+#elif defined (MDE_CPU_IPF)
+
+#define EFI_IMAGE_MACHINE_TYPE_SUPPORTED(Machine) \
+  (((Machine) == EFI_IMAGE_MACHINE_IPF) || ((Machine) == EFI_IMAGE_MACHINE_EBC))
+
+#define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) (FALSE)
+
+//
+// @bug - Remove me when other package updated.
+//
+typedef EFI_IMAGE_NT_HEADERS64    EFI_IMAGE_NT_HEADERS;
+
+#elif defined (MDE_CPU_X64)
+
+#define EFI_IMAGE_MACHINE_TYPE_SUPPORTED(Machine) \
+  (((Machine) == EFI_IMAGE_MACHINE_X64) || ((Machine) == EFI_IMAGE_MACHINE_EBC))
+
+#define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) ((Machine) == EFI_IMAGE_MACHINE_IA32)
+
+//
+// @bug - Remove me when other package updated.
+//
+typedef EFI_IMAGE_NT_HEADERS64    EFI_IMAGE_NT_HEADERS;
+
+#elif defined (MDE_CPU_EBC)
+
+///
+/// This is just to make sure you can cross compile with the EBC compiiler.
+/// It does not make sense to have a PE loader coded in EBC. You need to
+/// understand the basic
+///
+#define EFI_IMAGE_MACHINE_TYPE_SUPPORTED(Machine) ((Machine) == EFI_IMAGE_MACHINE_EBC)
+
+#define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) (FALSE)
+
+//
+// @bug - Remove me when other package updated.
+//
+typedef EFI_IMAGE_NT_HEADERS64    EFI_IMAGE_NT_HEADERS;
+
+#else
+#error Unknown Processor Type
+#endif
+
+
+#define EFI_IMAGE_FIRST_SECTION(ntheader) \
+    ( \
+      (EFI_IMAGE_SECTION_HEADER *) \
+        ( \
+          (UINT32) ntheader + \
+          FIELD_OFFSET (EFI_IMAGE_NT_HEADERS, OptionalHeader) + \
+          ((EFI_IMAGE_NT_HEADERS *) (ntheader))->FileHeader.SizeOfOptionalHeader \
+        ) \
+    )
+
+//
+// Subsystem Values
+//
+#define EFI_IMAGE_SUBSYSTEM_UNKNOWN     0
+#define EFI_IMAGE_SUBSYSTEM_NATIVE      1
+#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2
+#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3.
+#define EFI_IMAGE_SUBSYSTEM_OS2_CUI     5
+#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI   7
+
+//
+// Directory Entries
+//
+#define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT      0
+#define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT      1
+#define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE    2
+#define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION   3
+#define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY    4
+#define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC   5
+#define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG       6
+#define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT   7
+#define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR   8
+#define EFI_IMAGE_DIRECTORY_ENTRY_TLS         9
+#define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
+
+//
+// Section header format.
+//
+#define EFI_IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct {
+  UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME];
+  union {
+    UINT32  PhysicalAddress;
+    UINT32  VirtualSize;
+  } Misc;
+  UINT32  VirtualAddress;
+  UINT32  SizeOfRawData;
+  UINT32  PointerToRawData;
+  UINT32  PointerToRelocations;
+  UINT32  PointerToLinenumbers;
+  UINT16  NumberOfRelocations;
+  UINT16  NumberOfLinenumbers;
+  UINT32  Characteristics;
+} EFI_IMAGE_SECTION_HEADER;
+
+#define EFI_IMAGE_SIZEOF_SECTION_HEADER       40
+
+#define EFI_IMAGE_SCN_TYPE_NO_PAD             0x00000008  // Reserved.
+#define EFI_IMAGE_SCN_CNT_CODE                0x00000020
+#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA    0x00000040
+#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA  0x00000080
+
+#define EFI_IMAGE_SCN_LNK_OTHER               0x00000100  // Reserved.
+#define EFI_IMAGE_SCN_LNK_INFO                0x00000200  // Section contains comments or some other type of information.
+#define EFI_IMAGE_SCN_LNK_REMOVE              0x00000800  // Section contents will not become part of image.
+#define EFI_IMAGE_SCN_LNK_COMDAT              0x00001000
+
+#define EFI_IMAGE_SCN_ALIGN_1BYTES            0x00100000
+#define EFI_IMAGE_SCN_ALIGN_2BYTES            0x00200000
+#define EFI_IMAGE_SCN_ALIGN_4BYTES            0x00300000
+#define EFI_IMAGE_SCN_ALIGN_8BYTES            0x00400000
+#define EFI_IMAGE_SCN_ALIGN_16BYTES           0x00500000
+#define EFI_IMAGE_SCN_ALIGN_32BYTES           0x00600000
+#define EFI_IMAGE_SCN_ALIGN_64BYTES           0x00700000
+
+#define EFI_IMAGE_SCN_MEM_DISCARDABLE         0x02000000
+#define EFI_IMAGE_SCN_MEM_NOT_CACHED          0x04000000
+#define EFI_IMAGE_SCN_MEM_NOT_PAGED           0x08000000
+#define EFI_IMAGE_SCN_MEM_SHARED              0x10000000
+#define EFI_IMAGE_SCN_MEM_EXECUTE             0x20000000
+#define EFI_IMAGE_SCN_MEM_READ                0x40000000
+#define EFI_IMAGE_SCN_MEM_WRITE               0x80000000
+
+///
+/// Symbol format.
+///
+#define EFI_IMAGE_SIZEOF_SYMBOL 18
+
+//
+// Section values.
+//
+// Symbols have a section number of the section in which they are
+// defined. Otherwise, section numbers have the following meanings:
+//
+#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0  // Symbol is undefined or is common.
+#define EFI_IMAGE_SYM_ABSOLUTE  (UINT16) -1 // Symbol is an absolute value.
+#define EFI_IMAGE_SYM_DEBUG     (UINT16) -2 // Symbol is a special debug item.
+//
+// Type (fundamental) values.
+//
+#define EFI_IMAGE_SYM_TYPE_NULL   0   // no type.
+#define EFI_IMAGE_SYM_TYPE_VOID   1   //
+#define EFI_IMAGE_SYM_TYPE_CHAR   2   // type character.
+#define EFI_IMAGE_SYM_TYPE_SHORT  3   // type short integer.
+#define EFI_IMAGE_SYM_TYPE_INT    4
+#define EFI_IMAGE_SYM_TYPE_LONG   5
+#define EFI_IMAGE_SYM_TYPE_FLOAT  6
+#define EFI_IMAGE_SYM_TYPE_DOUBLE 7
+#define EFI_IMAGE_SYM_TYPE_STRUCT 8
+#define EFI_IMAGE_SYM_TYPE_UNION  9
+#define EFI_IMAGE_SYM_TYPE_ENUM   10  // enumeration.
+#define EFI_IMAGE_SYM_TYPE_MOE    11  // member of enumeration.
+#define EFI_IMAGE_SYM_TYPE_BYTE   12
+#define EFI_IMAGE_SYM_TYPE_WORD   13
+#define EFI_IMAGE_SYM_TYPE_UINT   14
+#define EFI_IMAGE_SYM_TYPE_DWORD  15
+
+//
+// Type (derived) values.
+//
+#define EFI_IMAGE_SYM_DTYPE_NULL      0 // no derived type.
+#define EFI_IMAGE_SYM_DTYPE_POINTER   1
+#define EFI_IMAGE_SYM_DTYPE_FUNCTION  2
+#define EFI_IMAGE_SYM_DTYPE_ARRAY     3
+
+//
+// Storage classes.
+//
+#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION   (UINT8) -1
+#define EFI_IMAGE_SYM_CLASS_NULL              0
+#define EFI_IMAGE_SYM_CLASS_AUTOMATIC         1
+#define EFI_IMAGE_SYM_CLASS_EXTERNAL          2
+#define EFI_IMAGE_SYM_CLASS_STATIC            3
+#define EFI_IMAGE_SYM_CLASS_REGISTER          4
+#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF      5
+#define EFI_IMAGE_SYM_CLASS_LABEL             6
+#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL   7
+#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT  8
+#define EFI_IMAGE_SYM_CLASS_ARGUMENT          9
+#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG        10
+#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION   11
+#define EFI_IMAGE_SYM_CLASS_UNION_TAG         12
+#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION   13
+#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC  14
+#define EFI_IMAGE_SYM_CLASS_ENUM_TAG          15
+#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM    16
+#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM    17
+#define EFI_IMAGE_SYM_CLASS_BIT_FIELD         18
+#define EFI_IMAGE_SYM_CLASS_BLOCK             100
+#define EFI_IMAGE_SYM_CLASS_FUNCTION          101
+#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT     102
+#define EFI_IMAGE_SYM_CLASS_FILE              103
+#define EFI_IMAGE_SYM_CLASS_SECTION           104
+#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL     105
+
+//
+// type packing constants
+//
+#define EFI_IMAGE_N_BTMASK  017
+#define EFI_IMAGE_N_TMASK   060
+#define EFI_IMAGE_N_TMASK1  0300
+#define EFI_IMAGE_N_TMASK2  0360
+#define EFI_IMAGE_N_BTSHFT  4
+#define EFI_IMAGE_N_TSHIFT  2
+
+//
+// Communal selection types.
+//
+#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES    1
+#define EFI_IMAGE_COMDAT_SELECT_ANY             2
+#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE       3
+#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH     4
+#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE     5
+
+#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY  1
+#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY    2
+#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS      3
+
+///
+/// Relocation format.
+///
+typedef struct {
+  UINT32  VirtualAddress;
+  UINT32  SymbolTableIndex;
+  UINT16  Type;
+} EFI_IMAGE_RELOCATION;
+
+#define EFI_IMAGE_SIZEOF_RELOCATION 10
+
+//
+// I386 relocation types.
+//
+#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000   // Reference is absolute, no relocation is necessary
+#define EFI_IMAGE_REL_I386_DIR16    0x0001  // Direct 16-bit reference to the symbols virtual address
+#define EFI_IMAGE_REL_I386_REL16    0x0002  // PC-relative 16-bit reference to the symbols virtual address
+#define EFI_IMAGE_REL_I386_DIR32    0x0006  // Direct 32-bit reference to the symbols virtual address
+#define EFI_IMAGE_REL_I386_DIR32NB  0x0007  // Direct 32-bit reference to the symbols virtual address, base not included
+#define EFI_IMAGE_REL_I386_SEG12    0x0009 // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address
+#define EFI_IMAGE_REL_I386_SECTION  0x001a
+#define EFI_IMAGE_REL_I386_SECREL   0x000b
+#define EFI_IMAGE_REL_I386_REL32    0x0014 // PC-relative 32-bit reference to the symbols virtual address
+
+//
+// x64 processor relocation types.
+//
+#define IMAGE_REL_AMD64_ABSOLUTE       0x0000
+#define IMAGE_REL_AMD64_ADDR64   0x0001
+#define IMAGE_REL_AMD64_ADDR32   0x0002
+#define IMAGE_REL_AMD64_ADDR32NB       0x0003
+#define IMAGE_REL_AMD64_REL32      0x0004
+#define IMAGE_REL_AMD64_REL32_1          0x0005
+#define IMAGE_REL_AMD64_REL32_2          0x0006
+#define IMAGE_REL_AMD64_REL32_3          0x0007
+#define IMAGE_REL_AMD64_REL32_4          0x0008
+#define IMAGE_REL_AMD64_REL32_5          0x0009
+#define IMAGE_REL_AMD64_SECTION          0x000A
+#define IMAGE_REL_AMD64_SECREL   0x000B
+#define IMAGE_REL_AMD64_SECREL7          0x000C
+#define IMAGE_REL_AMD64_TOKEN      0x000D
+#define IMAGE_REL_AMD64_SREL32   0x000E
+#define IMAGE_REL_AMD64_PAIR       0x000F
+#define IMAGE_REL_AMD64_SSPAN32          0x0010
+
+///
+/// Based relocation format.
+///
+typedef struct {
+  UINT32  VirtualAddress;
+  UINT32  SizeOfBlock;
+} EFI_IMAGE_BASE_RELOCATION;
+
+#define EFI_IMAGE_SIZEOF_BASE_RELOCATION  8
+
+//
+// Based relocation types.
+//
+#define EFI_IMAGE_REL_BASED_ABSOLUTE      0
+#define EFI_IMAGE_REL_BASED_HIGH          1
+#define EFI_IMAGE_REL_BASED_LOW           2
+#define EFI_IMAGE_REL_BASED_HIGHLOW       3
+#define EFI_IMAGE_REL_BASED_HIGHADJ       4
+#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR  5
+#define EFI_IMAGE_REL_BASED_IA64_IMM64    9
+#define EFI_IMAGE_REL_BASED_DIR64         10
+
+///
+/// Line number format.
+///
+typedef struct {
+  union {
+    UINT32  SymbolTableIndex; // Symbol table index of function name if Linenumber is 0.
+    UINT32  VirtualAddress;   // Virtual address of line number.
+  } Type;
+  UINT16  Linenumber;         // Line number.
+} EFI_IMAGE_LINENUMBER;
+
+#define EFI_IMAGE_SIZEOF_LINENUMBER 6
+
+//
+// Archive format.
+//
+#define EFI_IMAGE_ARCHIVE_START_SIZE        8
+#define EFI_IMAGE_ARCHIVE_START             "!<arch>\n"
+#define EFI_IMAGE_ARCHIVE_END               "`\n"
+#define EFI_IMAGE_ARCHIVE_PAD               "\n"
+#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER     "/               "
+#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER  "//              "
+
+typedef struct {
+  UINT8 Name[16];     // File member name - `/' terminated.
+  UINT8 Date[12];     // File member date - decimal.
+  UINT8 UserID[6];    // File member user id - decimal.
+  UINT8 GroupID[6];   // File member group id - decimal.
+  UINT8 Mode[8];      // File member mode - octal.
+  UINT8 Size[10];     // File member size - decimal.
+  UINT8 EndHeader[2]; // String to end header.
+} EFI_IMAGE_ARCHIVE_MEMBER_HEADER;
+
+#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
+
+//
+// DLL support.
+//
+
+///
+/// DLL Export Format
+///
+typedef struct {
+  UINT32  Characteristics;
+  UINT32  TimeDateStamp;
+  UINT16  MajorVersion;
+  UINT16  MinorVersion;
+  UINT32  Name;
+  UINT32  Base;
+  UINT32  NumberOfFunctions;
+  UINT32  NumberOfNames;
+  UINT32  AddressOfFunctions;
+  UINT32  AddressOfNames;
+  UINT32  AddressOfNameOrdinals;
+} EFI_IMAGE_EXPORT_DIRECTORY;
+
+///
+/// DLL support.
+/// Import Format
+///
+typedef struct {
+  UINT16  Hint;
+  UINT8   Name[1];
+} EFI_IMAGE_IMPORT_BY_NAME;
+
+typedef struct {
+  union {
+    UINT32                    Function;
+    UINT32                    Ordinal;
+    EFI_IMAGE_IMPORT_BY_NAME  *AddressOfData;
+  } u1;
+} EFI_IMAGE_THUNK_DATA;
+
+#define EFI_IMAGE_ORDINAL_FLAG              0x80000000
+#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal)  ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0)
+#define EFI_IMAGE_ORDINAL(Ordinal)          (Ordinal & 0xffff)
+
+typedef struct {
+  UINT32                Characteristics;
+  UINT32                TimeDateStamp;
+  UINT32                ForwarderChain;
+  UINT32                Name;
+  EFI_IMAGE_THUNK_DATA  *FirstThunk;
+} EFI_IMAGE_IMPORT_DESCRIPTOR;
+
+///
+/// Debug Format
+///
+#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2
+
+typedef struct {
+  UINT32  Characteristics;
+  UINT32  TimeDateStamp;
+  UINT16  MajorVersion;
+  UINT16  MinorVersion;
+  UINT32  Type;
+  UINT32  SizeOfData;
+  UINT32  RVA;
+  UINT32  FileOffset;
+} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY;
+
+#define CODEVIEW_SIGNATURE_NB10 0x3031424E  // "NB10"
+typedef struct {
+  UINT32  Signature;                        // "NB10"
+  UINT32  Unknown;
+  UINT32  Unknown2;
+  UINT32  Unknown3;
+  //
+  // Filename of .PDB goes here
+  //
+} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY;
+
+#define CODEVIEW_SIGNATURE_RSDS 0x53445352  // "RSDS"
+typedef struct {
+  UINT32  Signature;                        // "RSDS"
+  UINT32  Unknown;
+  UINT32  Unknown2;
+  UINT32  Unknown3;
+  UINT32  Unknown4;
+  UINT32  Unknown5;
+  //
+  // Filename of .PDB goes here
+  //
+} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY;
+
+///
+/// Header format for TE images
+///
+typedef struct {
+  UINT16                    Signature;            // signature for TE format = "VZ"
+  UINT16                    Machine;              // from the original file header
+  UINT8                     NumberOfSections;     // from the original file header
+  UINT8                     Subsystem;            // from original optional header
+  UINT16                    StrippedSize;         // how many bytes we removed from the header
+  UINT32                    AddressOfEntryPoint;  // offset to entry point -- from original optional header
+  UINT32                    BaseOfCode;           // from original image -- required for ITP debug
+  UINT64                    ImageBase;            // from original file header
+  EFI_IMAGE_DATA_DIRECTORY  DataDirectory[2];     // only base relocation and debug directory
+} EFI_TE_IMAGE_HEADER;
+
+#define EFI_TE_IMAGE_HEADER_SIGNATURE 0x5A56      // "VZ"
+
+//
+// Data directory indexes in our TE image header
+//
+#define EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC  0
+#define EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG      1
+
+
+///
+/// Union of PE32, PE32+, and TE headers
+///
+typedef union {
+  EFI_IMAGE_NT_HEADERS32   Pe32;
+  EFI_IMAGE_NT_HEADERS64   Pe32Plus;
+  EFI_TE_IMAGE_HEADER      Te;
+} EFI_IMAGE_OPTIONAL_HEADER_UNION;
+
+typedef union {
+  EFI_IMAGE_NT_HEADERS32            *Pe32;
+  EFI_IMAGE_NT_HEADERS64            *Pe32Plus;
+  EFI_TE_IMAGE_HEADER               *Te;
+  EFI_IMAGE_OPTIONAL_HEADER_UNION   *Union;
+} EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION;
+
+#endif
diff --git a/src/include/gpxe/efi/Protocol/Cpu.h b/src/include/gpxe/efi/Protocol/Cpu.h
new file mode 100644 (file)
index 0000000..6052a34
--- /dev/null
@@ -0,0 +1,326 @@
+/** @file
+  CPU Architectural Protocol as defined in PI spec Volume 2 DXE
+
+  This code abstracts the DXE core from processor implementation details.
+
+  Copyright (c) 2006 - 2008, Intel Corporation
+  All rights reserved. This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __ARCH_PROTOCOL_CPU_H__
+#define __ARCH_PROTOCOL_CPU_H__
+
+#include <gpxe/efi/Protocol/DebugSupport.h>
+
+#define EFI_CPU_ARCH_PROTOCOL_GUID \
+  { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } }
+
+typedef struct _EFI_CPU_ARCH_PROTOCOL   EFI_CPU_ARCH_PROTOCOL;
+
+typedef enum {
+  EfiCpuFlushTypeWriteBackInvalidate,
+  EfiCpuFlushTypeWriteBack,
+  EfiCpuFlushTypeInvalidate,
+  EfiCpuMaxFlushType
+} EFI_CPU_FLUSH_TYPE;
+
+typedef enum {
+  EfiCpuInit,
+  EfiCpuMaxInitType
+} EFI_CPU_INIT_TYPE;
+
+/**
+  EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
+
+  @param  InterruptType    Defines the type of interrupt or exception that
+                           occurred on the processor.This parameter is processor architecture specific.
+  @param  SystemContext    A pointer to the processor context when
+                           the interrupt occurred on the processor.
+
+  @return None
+
+**/
+typedef
+VOID
+(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)(
+  IN CONST  EFI_EXCEPTION_TYPE  InterruptType,
+  IN CONST  EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  This function flushes the range of addresses from Start to Start+Length
+  from the processor's data cache. If Start is not aligned to a cache line
+  boundary, then the bytes before Start to the preceding cache line boundary
+  are also flushed. If Start+Length is not aligned to a cache line boundary,
+  then the bytes past Start+Length to the end of the next cache line boundary
+  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
+  supported. If the data cache is fully coherent with all DMA operations, then
+  this function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of the data cache, then the entire data cache can be flushed.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  Start            The beginning physical address to flush from the processor's data
+                           cache.
+  @param  Length           The number of bytes to flush from the processor's data cache. This
+                           function may flush more bytes than Length specifies depending upon
+                           the granularity of the flush operation that the processor supports.
+  @param  FlushType        Specifies the type of flush operation to perform.
+
+  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
+                                the processor's data cache.
+  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
+                                by FlushType.
+  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
+                                from the processor's data cache.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  IN EFI_PHYSICAL_ADDRESS               Start,
+  IN UINT64                             Length,
+  IN EFI_CPU_FLUSH_TYPE                 FlushType
+  );
+
+
+/**
+  This function enables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This
+  );
+
+
+/**
+  This function disables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This
+  );
+
+
+/**
+  This function retrieves the processor's current interrupt state a returns it in
+  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
+  are currently disabled, then FALSE is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
+                           interrupts are enabled and FALSE if interrupts are disabled.
+
+  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
+  @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  OUT BOOLEAN                           *State
+  );
+
+
+/**
+  This function generates an INIT on the processor. If this function succeeds, then the
+  processor will be reset, and control will not be returned to the caller. If InitType is
+  not supported by this processor, or the processor cannot programmatically generate an
+  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
+  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InitType         The type of processor INIT to perform.
+
+  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
+  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
+                                by this processor.
+  @retval EFI_DEVICE_ERROR      The processor INIT failed.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_INIT)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  IN EFI_CPU_INIT_TYPE                  InitType
+  );
+
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
+                           are enabled and FALSE if interrupts are disabled.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  IN EFI_EXCEPTION_TYPE                 InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER          InterruptHandler
+  );
+
+
+/**
+  This function reads the processor timer specified by TimerIndex and returns it in TimerValue.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  TimerIndex       Specifies which processor timer is to be returned in TimerValue. This parameter
+                           must be between 0 and NumberOfTimers-1.
+  @param  TimerValue       Pointer to the returned timer value.
+  @param  TimerPeriod      A pointer to the amount of time that passes in femtoseconds for each increment
+                           of TimerValue.
+
+  @retval EFI_SUCCESS           The processor timer value specified by TimerIndex was returned in TimerValue.
+  @retval EFI_DEVICE_ERROR      An error occurred attempting to read one of the processor's timers.
+  @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid.
+  @retval EFI_UNSUPPORTED       The processor does not have any readable timers.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_GET_TIMER_VALUE)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  IN UINT32                             TimerIndex,
+  OUT UINT64                            *TimerValue,
+  OUT UINT64                            *TimerPeriod OPTIONAL
+  );
+
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)(
+  IN EFI_CPU_ARCH_PROTOCOL              *This,
+  IN  EFI_PHYSICAL_ADDRESS              BaseAddress,
+  IN  UINT64                            Length,
+  IN  UINT64                            Attributes
+  );
+
+
+/**
+  @par Protocol Description:
+  The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE
+  Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt
+  vectors and exception vectors, reading internal processor timers, resetting the processor, and
+  determining the processor frequency.
+
+  @param FlushDataCache
+  Flushes a range of the processor's data cache. If the processor does
+  not contain a data cache, or the data cache is fully coherent, then this
+  function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of addresses from the data cache, then the entire data
+  cache must be flushed.
+
+  @param EnableInterrupt
+  Enables interrupt processing by the processor.
+
+  @param DisableInterrupt
+  Disables interrupt processing by the processor.
+
+  @param GetInterruptState
+  Retrieves the processor's current interrupt state.
+
+  @param Init
+  Generates an INIT on the processor. If a processor cannot programmatically
+  generate an INIT without help from external hardware, then this function
+  returns EFI_UNSUPPORTED.
+
+  @param RegisterInterruptHandler
+  Associates an interrupt service routine with one of the processor's interrupt
+  vectors. This function is typically used by the EFI_TIMER_ARCH_PROTOCOL to
+  hook the timer interrupt in a system. It can also be used by the debugger to
+  hook exception vectors.
+
+  @param GetTimerValue
+  Returns the value of one of the processor's internal timers.
+
+  @param SetMemoryAttributes
+  Attempts to set the attributes of a memory region.
+
+  @param NumberOfTimers
+  The number of timers that are available in a processor. The value in this
+  field is a constant that must not be modified after the CPU Architectural
+  Protocol is installed. All consumers must treat this as a read-only field.
+
+  @param DmaBufferAlignment
+  The size, in bytes, of the alignment required for DMA buffer allocations.
+  This is typically the size of the largest data cache line in the platform.
+  The value in this field is a constant that must not be modified after the
+  CPU Architectural Protocol is installed. All consumers must treat this as
+  a read-only field.
+
+**/
+struct _EFI_CPU_ARCH_PROTOCOL {
+  EFI_CPU_FLUSH_DATA_CACHE            FlushDataCache;
+  EFI_CPU_ENABLE_INTERRUPT            EnableInterrupt;
+  EFI_CPU_DISABLE_INTERRUPT           DisableInterrupt;
+  EFI_CPU_GET_INTERRUPT_STATE         GetInterruptState;
+  EFI_CPU_INIT                        Init;
+  EFI_CPU_REGISTER_INTERRUPT_HANDLER  RegisterInterruptHandler;
+  EFI_CPU_GET_TIMER_VALUE             GetTimerValue;
+  EFI_CPU_SET_MEMORY_ATTRIBUTES       SetMemoryAttributes;
+  UINT32                              NumberOfTimers;
+  UINT32                              DmaBufferAlignment;
+};
+
+extern EFI_GUID gEfiCpuArchProtocolGuid;
+
+#endif
diff --git a/src/include/gpxe/efi/Protocol/CpuIo.h b/src/include/gpxe/efi/Protocol/CpuIo.h
new file mode 100644 (file)
index 0000000..8d35c6c
--- /dev/null
@@ -0,0 +1,128 @@
+/** @file
+  This code abstracts the CPU IO Protocol which installed by some platform or chipset-specific
+  PEIM that abstracts the processor-visible I/O operations.
+
+  Copyright (c) 2007, Intel Corporation
+  All rights reserved. This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+  Module Name:  CpuIO.h
+
+  @par Revision Reference:
+  CPU IO Protocol is defined in Framework of EFI CPU IO Protocol Spec
+  Version 0.9
+
+**/
+
+#ifndef _CPUIO_H_
+#define _CPUIO_H_
+
+#include <gpxe/efi/PiDxe.h>
+
+#define EFI_CPU_IO_PROTOCOL_GUID \
+  { \
+    0xB0732526, 0x38C8, 0x4b40, {0x88, 0x77, 0x61, 0xC7, 0xB0, 0x6A, 0xAC, 0x45 } \
+  }
+
+typedef struct _EFI_CPU_IO_PROTOCOL EFI_CPU_IO_PROTOCOL;
+
+//
+// *******************************************************
+// EFI_CPU_IO_PROTOCOL_WIDTH
+// *******************************************************
+//
+typedef enum {
+  EfiCpuIoWidthUint8,
+  EfiCpuIoWidthUint16,
+  EfiCpuIoWidthUint32,
+  EfiCpuIoWidthUint64,
+  EfiCpuIoWidthFifoUint8,
+  EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32,
+  EfiCpuIoWidthFifoUint64,
+  EfiCpuIoWidthFillUint8,
+  EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32,
+  EfiCpuIoWidthFillUint64,
+  EfiCpuIoWidthMaximum
+} EFI_CPU_IO_PROTOCOL_WIDTH;
+
+//
+// *******************************************************
+// EFI_CPU_IO_PROTOCOL_IO_MEM
+// *******************************************************
+//
+/**
+  Enables a driver to access memory-mapped registers in the EFI system memory space.
+  Or, Enables a driver to access registers in the EFI CPU I/O space.
+
+  @param  This                  A pointer to the EFI_CPU_IO_PROTOCOL instance.
+  @param  Width                 Signifies the width of the I/O or Memory operation.
+  @param  Address               The base address of the I/O or Memoryoperation.
+  @param  Count                 The number of I/O or Memory operations to perform.
+                                The number of bytes moved is Width size * Count, starting at Address.
+  @param  Buffer                For read operations, the destination buffer to store the results.
+                                For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS           The data was read from or written to the EFI system.
+  @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL.
+  @retval EFI_UNSUPPORTED       The Buffer is not aligned for the given Width.
+                                Or,The address range specified by Address, Width, and Count is not valid for this EFI system.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CPU_IO_PROTOCOL_IO_MEM)(
+  IN EFI_CPU_IO_PROTOCOL                *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
+  IN  UINT64                            Address,
+  IN  UINTN                             Count,
+  IN  OUT VOID                          *Buffer
+  );
+
+//
+// *******************************************************
+// EFI_CPU_IO_PROTOCOL_ACCESS
+// *******************************************************
+//
+typedef struct {
+  EFI_CPU_IO_PROTOCOL_IO_MEM  Read;
+  EFI_CPU_IO_PROTOCOL_IO_MEM  Write;
+} EFI_CPU_IO_PROTOCOL_ACCESS;
+
+//
+// *******************************************************
+// EFI_CPU_IO_PROTOCOL
+// *******************************************************
+//
+/**
+  @par Protocol Description:
+  Provides the basic memory and I/O interfaces that are used to abstract
+  accesses to devices in a system.
+
+  @param Mem.Read
+  Allows reads from memory-mapped I/O space.
+
+  @param Mem.Write
+  Allows writes to memory-mapped I/O space.
+
+  @param Io.Read
+  Allows reads from I/O space.
+
+  @param Io.Write
+  Allows writes to I/O space.
+
+**/
+struct _EFI_CPU_IO_PROTOCOL {
+  EFI_CPU_IO_PROTOCOL_ACCESS  Mem;
+  EFI_CPU_IO_PROTOCOL_ACCESS  Io;
+};
+
+extern EFI_GUID gEfiCpuIoProtocolGuid;
+
+#endif
diff --git a/src/include/gpxe/efi/Protocol/DebugSupport.h b/src/include/gpxe/efi/Protocol/DebugSupport.h
new file mode 100644 (file)
index 0000000..6f785a1
--- /dev/null
@@ -0,0 +1,656 @@
+/** @file
+  DebugSupport protocol and supporting definitions as defined in the UEFI2.0
+  specification.
+
+  The DebugSupport protocol is used by source level debuggers to abstract the
+  processor and handle context save and restore operations.
+
+  Copyright (c) 2006 - 2008, Intel Corporation
+  All rights reserved. This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __DEBUG_SUPPORT_H__
+#define __DEBUG_SUPPORT_H__
+
+#include <gpxe/efi/ProcessorBind.h>
+#include <gpxe/efi/IndustryStandard/PeImage.h>
+
+typedef struct _EFI_DEBUG_SUPPORT_PROTOCOL EFI_DEBUG_SUPPORT_PROTOCOL;
+
+///
+/// Debug Support protocol {2755590C-6F3C-42FA-9EA4-A3BA543CDA25}
+///
+#define EFI_DEBUG_SUPPORT_PROTOCOL_GUID \
+  { \
+    0x2755590C, 0x6F3C, 0x42FA, {0x9E, 0xA4, 0xA3, 0xBA, 0x54, 0x3C, 0xDA, 0x25 } \
+  }
+
+///
+/// Debug Support definitions
+///
+typedef INTN  EFI_EXCEPTION_TYPE;
+
+//
+//  IA-32 processor exception types
+//
+#define EXCEPT_IA32_DIVIDE_ERROR    0
+#define EXCEPT_IA32_DEBUG           1
+#define EXCEPT_IA32_NMI             2
+#define EXCEPT_IA32_BREAKPOINT      3
+#define EXCEPT_IA32_OVERFLOW        4
+#define EXCEPT_IA32_BOUND           5
+#define EXCEPT_IA32_INVALID_OPCODE  6
+#define EXCEPT_IA32_DOUBLE_FAULT    8
+#define EXCEPT_IA32_INVALID_TSS     10
+#define EXCEPT_IA32_SEG_NOT_PRESENT 11
+#define EXCEPT_IA32_STACK_FAULT     12
+#define EXCEPT_IA32_GP_FAULT        13
+#define EXCEPT_IA32_PAGE_FAULT      14
+#define EXCEPT_IA32_FP_ERROR        16
+#define EXCEPT_IA32_ALIGNMENT_CHECK 17
+#define EXCEPT_IA32_MACHINE_CHECK   18
+#define EXCEPT_IA32_SIMD            19
+
+//
+//  IA-32 processor context definition
+//
+//
+// FXSAVE_STATE
+// FP / MMX / XMM registers (see fxrstor instruction definition)
+//
+typedef struct {
+  UINT16  Fcw;
+  UINT16  Fsw;
+  UINT16  Ftw;
+  UINT16  Opcode;
+  UINT32  Eip;
+  UINT16  Cs;
+  UINT16  Reserved1;
+  UINT32  DataOffset;
+  UINT16  Ds;
+  UINT8   Reserved2[10];
+  UINT8   St0Mm0[10], Reserved3[6];
+  UINT8   St1Mm1[10], Reserved4[6];
+  UINT8   St2Mm2[10], Reserved5[6];
+  UINT8   St3Mm3[10], Reserved6[6];
+  UINT8   St4Mm4[10], Reserved7[6];
+  UINT8   St5Mm5[10], Reserved8[6];
+  UINT8   St6Mm6[10], Reserved9[6];
+  UINT8   St7Mm7[10], Reserved10[6];
+  UINT8   Xmm0[16];
+  UINT8   Xmm1[16];
+  UINT8   Xmm2[16];
+  UINT8   Xmm3[16];
+  UINT8   Xmm4[16];
+  UINT8   Xmm5[16];
+  UINT8   Xmm6[16];
+  UINT8   Xmm7[16];
+  UINT8   Reserved11[14 * 16];
+} EFI_FX_SAVE_STATE_IA32;
+
+typedef struct {
+  UINT32                 ExceptionData;
+  EFI_FX_SAVE_STATE_IA32 FxSaveState;
+  UINT32                 Dr0;
+  UINT32                 Dr1;
+  UINT32                 Dr2;
+  UINT32                 Dr3;
+  UINT32                 Dr6;
+  UINT32                 Dr7;
+  UINT32                 Cr0;
+  UINT32                 Cr1;  /* Reserved */
+  UINT32                 Cr2;
+  UINT32                 Cr3;
+  UINT32                 Cr4;
+  UINT32                 Eflags;
+  UINT32                 Ldtr;
+  UINT32                 Tr;
+  UINT32                 Gdtr[2];
+  UINT32                 Idtr[2];
+  UINT32                 Eip;
+  UINT32                 Gs;
+  UINT32                 Fs;
+  UINT32                 Es;
+  UINT32                 Ds;
+  UINT32                 Cs;
+  UINT32                 Ss;
+  UINT32                 Edi;
+  UINT32                 Esi;
+  UINT32                 Ebp;
+  UINT32                 Esp;
+  UINT32                 Ebx;
+  UINT32                 Edx;
+  UINT32                 Ecx;
+  UINT32                 Eax;
+} EFI_SYSTEM_CONTEXT_IA32;
+
+//
+//  X64 processor exception types
+//
+#define EXCEPT_X64_DIVIDE_ERROR    0
+#define EXCEPT_X64_DEBUG           1
+#define EXCEPT_X64_NMI             2
+#define EXCEPT_X64_BREAKPOINT      3
+#define EXCEPT_X64_OVERFLOW        4
+#define EXCEPT_X64_BOUND           5
+#define EXCEPT_X64_INVALID_OPCODE  6
+#define EXCEPT_X64_DOUBLE_FAULT    8
+#define EXCEPT_X64_INVALID_TSS     10
+#define EXCEPT_X64_SEG_NOT_PRESENT 11
+#define EXCEPT_X64_STACK_FAULT     12
+#define EXCEPT_X64_GP_FAULT        13
+#define EXCEPT_X64_PAGE_FAULT      14
+#define EXCEPT_X64_FP_ERROR        16
+#define EXCEPT_X64_ALIGNMENT_CHECK 17
+#define EXCEPT_X64_MACHINE_CHECK   18
+#define EXCEPT_X64_SIMD            19
+
+//
+//  X64 processor context definition
+//
+// FXSAVE_STATE
+// FP / MMX / XMM registers (see fxrstor instruction definition)
+//
+typedef struct {
+  UINT16  Fcw;
+  UINT16  Fsw;
+  UINT16  Ftw;
+  UINT16  Opcode;
+  UINT64  Rip;
+  UINT64  DataOffset;
+  UINT8   Reserved1[8];
+  UINT8   St0Mm0[10], Reserved2[6];
+  UINT8   St1Mm1[10], Reserved3[6];
+  UINT8   St2Mm2[10], Reserved4[6];
+  UINT8   St3Mm3[10], Reserved5[6];
+  UINT8   St4Mm4[10], Reserved6[6];
+  UINT8   St5Mm5[10], Reserved7[6];
+  UINT8   St6Mm6[10], Reserved8[6];
+  UINT8   St7Mm7[10], Reserved9[6];
+  UINT8   Xmm0[16];
+  UINT8   Xmm1[16];
+  UINT8   Xmm2[16];
+  UINT8   Xmm3[16];
+  UINT8   Xmm4[16];
+  UINT8   Xmm5[16];
+  UINT8   Xmm6[16];
+  UINT8   Xmm7[16];
+  //
+  // NOTE: UEFI 2.0 spec definition as follows.
+  //
+  UINT8   Reserved11[14 * 16];
+} EFI_FX_SAVE_STATE_X64;
+
+typedef struct {
+  UINT64                ExceptionData;
+  EFI_FX_SAVE_STATE_X64 FxSaveState;
+  UINT64                Dr0;
+  UINT64                Dr1;
+  UINT64                Dr2;
+  UINT64                Dr3;
+  UINT64                Dr6;
+  UINT64                Dr7;
+  UINT64                Cr0;
+  UINT64                Cr1;  /* Reserved */
+  UINT64                Cr2;
+  UINT64                Cr3;
+  UINT64                Cr4;
+  UINT64                Cr8;
+  UINT64                Rflags;
+  UINT64                Ldtr;
+  UINT64                Tr;
+  UINT64                Gdtr[2];
+  UINT64                Idtr[2];
+  UINT64                Rip;
+  UINT64                Gs;
+  UINT64                Fs;
+  UINT64                Es;
+  UINT64                Ds;
+  UINT64                Cs;
+  UINT64                Ss;
+  UINT64                Rdi;
+  UINT64                Rsi;
+  UINT64                Rbp;
+  UINT64                Rsp;
+  UINT64                Rbx;
+  UINT64                Rdx;
+  UINT64                Rcx;
+  UINT64                Rax;
+  UINT64                R8;
+  UINT64                R9;
+  UINT64                R10;
+  UINT64                R11;
+  UINT64                R12;
+  UINT64                R13;
+  UINT64                R14;
+  UINT64                R15;
+} EFI_SYSTEM_CONTEXT_X64;
+
+//
+//  IPF processor exception types
+//
+#define EXCEPT_IPF_VHTP_TRANSLATION       0
+#define EXCEPT_IPF_INSTRUCTION_TLB        1
+#define EXCEPT_IPF_DATA_TLB               2
+#define EXCEPT_IPF_ALT_INSTRUCTION_TLB    3
+#define EXCEPT_IPF_ALT_DATA_TLB           4
+#define EXCEPT_IPF_DATA_NESTED_TLB        5
+#define EXCEPT_IPF_INSTRUCTION_KEY_MISSED 6
+#define EXCEPT_IPF_DATA_KEY_MISSED        7
+#define EXCEPT_IPF_DIRTY_BIT              8
+#define EXCEPT_IPF_INSTRUCTION_ACCESS_BIT 9
+#define EXCEPT_IPF_DATA_ACCESS_BIT        10
+#define EXCEPT_IPF_BREAKPOINT             11
+#define EXCEPT_IPF_EXTERNAL_INTERRUPT     12
+//
+// 13 - 19 reserved
+//
+#define EXCEPT_IPF_PAGE_NOT_PRESENT           20
+#define EXCEPT_IPF_KEY_PERMISSION             21
+#define EXCEPT_IPF_INSTRUCTION_ACCESS_RIGHTS  22
+#define EXCEPT_IPF_DATA_ACCESS_RIGHTS         23
+#define EXCEPT_IPF_GENERAL_EXCEPTION          24
+#define EXCEPT_IPF_DISABLED_FP_REGISTER       25
+#define EXCEPT_IPF_NAT_CONSUMPTION            26
+#define EXCEPT_IPF_SPECULATION                27
+//
+// 28 reserved
+//
+#define EXCEPT_IPF_DEBUG                          29
+#define EXCEPT_IPF_UNALIGNED_REFERENCE            30
+#define EXCEPT_IPF_UNSUPPORTED_DATA_REFERENCE     31
+#define EXCEPT_IPF_FP_FAULT                       32
+#define EXCEPT_IPF_FP_TRAP                        33
+#define EXCEPT_IPF_LOWER_PRIVILEGE_TRANSFER_TRAP  34
+#define EXCEPT_IPF_TAKEN_BRANCH                   35
+#define EXCEPT_IPF_SINGLE_STEP                    36
+//
+// 37 - 44 reserved
+//
+#define EXCEPT_IPF_IA32_EXCEPTION 45
+#define EXCEPT_IPF_IA32_INTERCEPT 46
+#define EXCEPT_IPF_IA32_INTERRUPT 47
+
+//
+//  IPF processor context definition
+//
+typedef struct {
+  //
+  // The first reserved field is necessary to preserve alignment for the correct
+  // bits in UNAT and to insure F2 is 16 byte aligned..
+  //
+  UINT64  Reserved;
+  UINT64  R1;
+  UINT64  R2;
+  UINT64  R3;
+  UINT64  R4;
+  UINT64  R5;
+  UINT64  R6;
+  UINT64  R7;
+  UINT64  R8;
+  UINT64  R9;
+  UINT64  R10;
+  UINT64  R11;
+  UINT64  R12;
+  UINT64  R13;
+  UINT64  R14;
+  UINT64  R15;
+  UINT64  R16;
+  UINT64  R17;
+  UINT64  R18;
+  UINT64  R19;
+  UINT64  R20;
+  UINT64  R21;
+  UINT64  R22;
+  UINT64  R23;
+  UINT64  R24;
+  UINT64  R25;
+  UINT64  R26;
+  UINT64  R27;
+  UINT64  R28;
+  UINT64  R29;
+  UINT64  R30;
+  UINT64  R31;
+
+  UINT64  F2[2];
+  UINT64  F3[2];
+  UINT64  F4[2];
+  UINT64  F5[2];
+  UINT64  F6[2];
+  UINT64  F7[2];
+  UINT64  F8[2];
+  UINT64  F9[2];
+  UINT64  F10[2];
+  UINT64  F11[2];
+  UINT64  F12[2];
+  UINT64  F13[2];
+  UINT64  F14[2];
+  UINT64  F15[2];
+  UINT64  F16[2];
+  UINT64  F17[2];
+  UINT64  F18[2];
+  UINT64  F19[2];
+  UINT64  F20[2];
+  UINT64  F21[2];
+  UINT64  F22[2];
+  UINT64  F23[2];
+  UINT64  F24[2];
+  UINT64  F25[2];
+  UINT64  F26[2];
+  UINT64  F27[2];
+  UINT64  F28[2];
+  UINT64  F29[2];
+  UINT64  F30[2];
+  UINT64  F31[2];
+
+  UINT64  Pr;
+
+  UINT64  B0;
+  UINT64  B1;
+  UINT64  B2;
+  UINT64  B3;
+  UINT64  B4;
+  UINT64  B5;
+  UINT64  B6;
+  UINT64  B7;
+
+  //
+  // application registers
+  //
+  UINT64  ArRsc;
+  UINT64  ArBsp;
+  UINT64  ArBspstore;
+  UINT64  ArRnat;
+
+  UINT64  ArFcr;
+
+  UINT64  ArEflag;
+  UINT64  ArCsd;
+  UINT64  ArSsd;
+  UINT64  ArCflg;
+  UINT64  ArFsr;
+  UINT64  ArFir;
+  UINT64  ArFdr;
+
+  UINT64  ArCcv;
+
+  UINT64  ArUnat;
+
+  UINT64  ArFpsr;
+
+  UINT64  ArPfs;
+  UINT64  ArLc;
+  UINT64  ArEc;
+
+  //
+  // control registers
+  //
+  UINT64  CrDcr;
+  UINT64  CrItm;
+  UINT64  CrIva;
+  UINT64  CrPta;
+  UINT64  CrIpsr;
+  UINT64  CrIsr;
+  UINT64  CrIip;
+  UINT64  CrIfa;
+  UINT64  CrItir;
+  UINT64  CrIipa;
+  UINT64  CrIfs;
+  UINT64  CrIim;
+  UINT64  CrIha;
+
+  //
+  // debug registers
+  //
+  UINT64  Dbr0;
+  UINT64  Dbr1;
+  UINT64  Dbr2;
+  UINT64  Dbr3;
+  UINT64  Dbr4;
+  UINT64  Dbr5;
+  UINT64  Dbr6;
+  UINT64  Dbr7;
+
+  UINT64  Ibr0;
+  UINT64  Ibr1;
+  UINT64  Ibr2;
+  UINT64  Ibr3;
+  UINT64  Ibr4;
+  UINT64  Ibr5;
+  UINT64  Ibr6;
+  UINT64  Ibr7;
+
+  //
+  // virtual registers - nat bits for R1-R31
+  //
+  UINT64  IntNat;
+
+} EFI_SYSTEM_CONTEXT_IPF;
+
+//
+//  EBC processor exception types
+//
+#define EXCEPT_EBC_UNDEFINED            0
+#define EXCEPT_EBC_DIVIDE_ERROR         1
+#define EXCEPT_EBC_DEBUG                2
+#define EXCEPT_EBC_BREAKPOINT           3
+#define EXCEPT_EBC_OVERFLOW             4
+#define EXCEPT_EBC_INVALID_OPCODE       5   // opcode out of range
+#define EXCEPT_EBC_STACK_FAULT          6
+#define EXCEPT_EBC_ALIGNMENT_CHECK      7
+#define EXCEPT_EBC_INSTRUCTION_ENCODING 8   // malformed instruction
+#define EXCEPT_EBC_BAD_BREAK            9   // BREAK 0 or undefined BREAK
+#define EXCEPT_EBC_STEP                 10  // to support debug stepping
+///
+/// For coding convenience, define the maximum valid EBC exception.
+///
+#define MAX_EBC_EXCEPTION EXCEPT_EBC_STEP
+
+///
+///  EBC processor context definition
+///
+typedef struct {
+  UINT64  R0;
+  UINT64  R1;
+  UINT64  R2;
+  UINT64  R3;
+  UINT64  R4;
+  UINT64  R5;
+  UINT64  R6;
+  UINT64  R7;
+  UINT64  Flags;
+  UINT64  ControlFlags;
+  UINT64  Ip;
+} EFI_SYSTEM_CONTEXT_EBC;
+
+///
+/// Universal EFI_SYSTEM_CONTEXT definition
+///
+typedef union {
+  EFI_SYSTEM_CONTEXT_EBC  *SystemContextEbc;
+  EFI_SYSTEM_CONTEXT_IA32 *SystemContextIa32;
+  EFI_SYSTEM_CONTEXT_X64  *SystemContextX64;
+  EFI_SYSTEM_CONTEXT_IPF  *SystemContextIpf;
+} EFI_SYSTEM_CONTEXT;
+
+//
+// DebugSupport callback function prototypes
+//
+
+/**
+  Registers and enables an exception callback function for the specified exception.
+
+  @param  ExceptionType         Exception types in EBC, IA-32, X64, or IPF
+  @param  SystemContext         Exception content.
+
+**/
+typedef
+VOID
+(*EFI_EXCEPTION_CALLBACK)(
+  IN     EFI_EXCEPTION_TYPE               ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT               SystemContext
+  );
+
+/**
+  Registers and enables the on-target debug agent's periodic entry point.
+
+  @param  SystemContext         Exception content.
+
+**/
+typedef
+VOID
+(*EFI_PERIODIC_CALLBACK)(
+  IN OUT EFI_SYSTEM_CONTEXT               SystemContext
+  );
+
+//
+// Machine type definition
+//
+typedef enum {
+  IsaIa32 = IMAGE_FILE_MACHINE_I386, // 0x014C
+  IsaX64  = IMAGE_FILE_MACHINE_X64,   // 0x8664
+  IsaIpf  = IMAGE_FILE_MACHINE_IA64,  // 0x0200
+  IsaEbc  = IMAGE_FILE_MACHINE_EBC    // 0x0EBC
+} EFI_INSTRUCTION_SET_ARCHITECTURE;
+
+
+//
+// DebugSupport member function definitions
+//
+
+/**
+  Returns the maximum value that may be used for the ProcessorIndex parameter in
+  RegisterPeriodicCallback() and RegisterExceptionCallback().
+
+  @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
+  @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the maximum supported
+                                processor index is returned.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_MAXIMUM_PROCESSOR_INDEX)(
+  IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
+  OUT UINTN                              *MaxProcessorIndex
+  );
+
+/**
+  Registers a function to be called back periodically in interrupt context.
+
+  @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
+  @param  ProcessorIndex        Specifies which processor the callback function applies to.
+  @param  PeriodicCallback      A pointer to a function of type PERIODIC_CALLBACK that is the main
+                                periodic entry point of the debug agent.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
+                                function was previously registered.
+  @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
+                                function.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REGISTER_PERIODIC_CALLBACK)(
+  IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
+  IN UINTN                               ProcessorIndex,
+  IN EFI_PERIODIC_CALLBACK               PeriodicCallback
+  );
+
+/**
+  Registers a function to be called when a given processor exception occurs.
+
+  @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
+  @param  ProcessorIndex        Specifies which processor the callback function applies to.
+  @param  PeriodicCallback      A pointer to a function of type EXCEPTION_CALLBACK that is called
+                                when the processor exception specified by ExceptionType occurs.
+  @param  ExceptionType         Specifies which processor exception to hook.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+  @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
+                                function was previously registered.
+  @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
+                                function.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REGISTER_EXCEPTION_CALLBACK)(
+  IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
+  IN UINTN                               ProcessorIndex,
+  IN EFI_EXCEPTION_CALLBACK              ExceptionCallback,
+  IN EFI_EXCEPTION_TYPE                  ExceptionType
+  );
+
+/**
+  Invalidates processor instruction cache for a memory range. Subsequent execution in this range
+  causes a fresh memory fetch to retrieve code to be executed.
+
+  @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
+  @param  ProcessorIndex        Specifies which processor's instruction cache is to be invalidated.
+  @param  Start                 Specifies the physical base of the memory range to be invalidated.
+  @param  Length                Specifies the minimum number of bytes in the processor's instruction
+                                cache to invalidate.
+
+  @retval EFI_SUCCESS           The function completed successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INVALIDATE_INSTRUCTION_CACHE)(
+  IN EFI_DEBUG_SUPPORT_PROTOCOL          *This,
+  IN UINTN                               ProcessorIndex,
+  IN VOID                                *Start,
+  IN UINT64                              Length
+  );
+
+//
+// DebugSupport protocol definition
+//
+/**
+  @par Protocol Description:
+  This protocol provides the services to allow the debug agent to register
+  callback functions that are called either periodically or when specific
+  processor exceptions occur.
+
+  @param Isa
+  Declares the processor architecture for this instance of the EFI
+  Debug Support protocol.
+
+  @param GetMaximumProcessorIndex
+  Returns the maximum processor index value that may be used.
+
+  @param RegisterPeriodicCallback
+  Registers a callback function that will be invoked periodically
+  and asynchronously to the execution of EFI.
+
+  @param RegisterExceptionCallback
+  Registers a callback function that will be called each time the
+  specified processor exception occurs.
+
+  @param InvalidateInstructionCache
+  Invalidate the instruction cache of the processor. This is required
+  by processor architectures where instruction and data caches are
+  not coherent when instructions in the code under debug has been
+  modified by the debug agent.
+**/
+struct _EFI_DEBUG_SUPPORT_PROTOCOL {
+  EFI_INSTRUCTION_SET_ARCHITECTURE  Isa;
+  EFI_GET_MAXIMUM_PROCESSOR_INDEX   GetMaximumProcessorIndex;
+  EFI_REGISTER_PERIODIC_CALLBACK    RegisterPeriodicCallback;
+  EFI_REGISTER_EXCEPTION_CALLBACK   RegisterExceptionCallback;
+  EFI_INVALIDATE_INSTRUCTION_CACHE  InvalidateInstructionCache;
+};
+
+extern EFI_GUID gEfiDebugSupportProtocolGuid;
+
+#endif
diff --git a/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h b/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h
new file mode 100644 (file)
index 0000000..c15c72f
--- /dev/null
@@ -0,0 +1,450 @@
+/** @file
+  PCI Root Bridge I/O protocol as defined in the UEFI 2.0 specification.
+
+  PCI Root Bridge I/O protocol is used by PCI Bus Driver to perform PCI Memory, PCI I/O,
+  and PCI Configuration cycles on a PCI Root Bridge. It also provides services to perform
+  defferent types of bus mastering DMA
+
+  Copyright (c) 2006 - 2008, Intel Corporation
+  All rights reserved. This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __PCI_ROOT_BRIDGE_IO_H__
+#define __PCI_ROOT_BRIDGE_IO_H__
+
+#include <gpxe/efi/PiDxe.h>
+
+#define EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID \
+  { \
+    0x2f707ebb, 0x4a1a, 0x11d4, {0x9a, 0x38, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
+  }
+
+typedef struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL;
+
+typedef enum {
+  EfiPciWidthUint8,
+  EfiPciWidthUint16,
+  EfiPciWidthUint32,
+  EfiPciWidthUint64,
+  EfiPciWidthFifoUint8,
+  EfiPciWidthFifoUint16,
+  EfiPciWidthFifoUint32,
+  EfiPciWidthFifoUint64,
+  EfiPciWidthFillUint8,
+  EfiPciWidthFillUint16,
+  EfiPciWidthFillUint32,
+  EfiPciWidthFillUint64,
+  EfiPciWidthMaximum
+} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH;
+
+typedef enum {
+  EfiPciOperationBusMasterRead,
+  EfiPciOperationBusMasterWrite,
+  EfiPciOperationBusMasterCommonBuffer,
+  EfiPciOperationBusMasterRead64,
+  EfiPciOperationBusMasterWrite64,
+  EfiPciOperationBusMasterCommonBuffer64,
+  EfiPciOperationMaximum
+} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION;
+
+#define EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO          0x0001
+#define EFI_PCI_ATTRIBUTE_ISA_IO                      0x0002
+#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO              0x0004
+#define EFI_PCI_ATTRIBUTE_VGA_MEMORY                  0x0008
+#define EFI_PCI_ATTRIBUTE_VGA_IO                      0x0010
+#define EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO              0x0020
+#define EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO            0x0040
+#define EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE        0x0080
+#define EFI_PCI_ATTRIBUTE_MEMORY_CACHED               0x0800
+#define EFI_PCI_ATTRIBUTE_MEMORY_DISABLE              0x1000
+#define EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE          0x8000
+
+#define EFI_PCI_ATTRIBUTE_VALID_FOR_ALLOCATE_BUFFER   (EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE | EFI_PCI_ATTRIBUTE_MEMORY_CACHED | EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE)
+
+#define EFI_PCI_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER (~EFI_PCI_ATTRIBUTE_VALID_FOR_ALLOCATE_BUFFER)
+
+#define EFI_PCI_ADDRESS(bus, dev, func, reg) \
+    ((UINT64) ((((UINTN) bus) << 24) + (((UINTN) dev) << 16) + (((UINTN) func) << 8) + ((UINTN) reg)))
+
+typedef struct {
+  UINT8   Register;
+  UINT8   Function;
+  UINT8   Device;
+  UINT8   Bus;
+  UINT32  ExtendedRegister;
+} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS;
+
+/**
+  Reads from the I/O space of a PCI Root Bridge. Returns when either the polling exit criteria is
+  satisfied or after a defined duration.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Width                 Signifies the width of the memory or I/O operations.
+  @param  Address               The base address of the memory or I/O operations.
+  @param  Mask                  Mask used for the polling criteria.
+  @param  Value                 The comparison value used for the polling exit criteria.
+  @param  Delay                 The number of 100 ns units to poll.
+  @param  Result                Pointer to the last value read from the memory location.
+
+  @retval EFI_SUCCESS           The last data returned from the access matched the poll exit criteria.
+  @retval EFI_TIMEOUT           Delay expired before a match occurred.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL           *This,
+  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH    Width,
+  IN  UINT64                                   Address,
+  IN  UINT64                                   Mask,
+  IN  UINT64                                   Value,
+  IN  UINT64                                   Delay,
+  OUT UINT64                                   *Result
+  );
+
+/**
+  Enables a PCI driver to access PCI controller registers in the PCI root bridge memory space.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Width                 Signifies the width of the memory operations.
+  @param  Address               The base address of the memory operations.
+  @param  Count                 The number of memory operations to perform.
+  @param  Buffer                For read operations, the destination buffer to store the results. For write
+                                operations, the source buffer to write data from.
+
+  @retval EFI_SUCCESS           The data was read from or written to the PCI root bridge.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL              *This,
+  IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH    Width,
+  IN     UINT64                                   Address,
+  IN     UINTN                                    Count,
+  IN OUT VOID                                     *Buffer
+  );
+
+typedef struct {
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM  Read;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM  Write;
+} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS;
+
+/**
+  Enables a PCI driver to copy one region of PCI root bridge memory space to another region of PCI
+  root bridge memory space.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL instance.
+  @param  Width                 Signifies the width of the memory operations.
+  @param  DestAddress           The destination address of the memory operation.
+  @param  SrcAddress            The source address of the memory operation.
+  @param  Count                 The number of memory operations to perform.
+
+  @retval EFI_SUCCESS           The data was copied from one memory region to another memory region.
+  @retval EFI_INVALID_PARAMETER Width is invalid for this PCI root bridge.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL              *This,
+  IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH    Width,
+  IN     UINT64                                   DestAddress,
+  IN     UINT64                                   SrcAddress,
+  IN     UINTN                                    Count
+  );
+
+/**
+  Provides the PCI controller-Cspecific addresses required to access system memory from a
+  DMA bus master.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Operation             Indicates if the bus master is going to read or write to system memory.
+  @param  HostAddress           The system memory address to map to the PCI controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
+                                that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL                *This,
+  IN     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION  Operation,
+  IN     VOID                                       *HostAddress,
+  IN OUT UINTN                                      *NumberOfBytes,
+  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
+  OUT    VOID                                       **Mapping
+  );
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL           *This,
+  IN  VOID                                     *Mapping
+  );
+
+/**
+  Allocates pages that are suitable for an EfiPciOperationBusMasterCommonBuffer or
+  EfiPciOperationBusMasterCommonBuffer64 mapping.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Type                  This parameter is not used and must be ignored.
+  @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
+                                EfiRuntimeServicesData.
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory address of the
+                                allocated range.
+  @param  Attributes            The requested bit mask of attributes for the allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL              *This,
+  IN     EFI_ALLOCATE_TYPE                        Type,
+  IN     EFI_MEMORY_TYPE                          MemoryType,
+  IN     UINTN                                    Pages,
+  IN OUT VOID                                     **HostAddress,
+  IN     UINT64                                   Attributes
+  );
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
+                                was not allocated with AllocateBuffer().
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL           *This,
+  IN  UINTN                                    Pages,
+  IN  VOID                                     *HostAddress
+  );
+
+/**
+  Flushes all PCI posted write transactions from a PCI host bridge to system memory.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+
+  @retval EFI_SUCCESS           The PCI posted write transactions were flushed from the PCI host
+                                bridge to system memory.
+  @retval EFI_DEVICE_ERROR      The PCI posted write transactions were not flushed from the PCI
+                                host bridge due to a hardware error.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *This
+  );
+
+/**
+  Gets the attributes that a PCI root bridge supports setting with SetAttributes(), and the
+  attributes that a PCI root bridge is currently using.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Supports              A pointer to the mask of attributes that this PCI root bridge supports
+                                setting with SetAttributes().
+  @param  Attributes            A pointer to the mask of attributes that this PCI root bridge is currently
+                                using.
+
+  @retval EFI_SUCCESS           If Supports is not NULL, then the attributes that the PCI root
+                                bridge supports is returned in Supports. If Attributes is
+                                not NULL, then the attributes that the PCI root bridge is currently
+                                using is returned in Attributes.
+  @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL.
+
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL           *This,
+  OUT UINT64                                   *Supports,
+  OUT UINT64                                   *Attributes
+  );
+
+/**
+  Sets attributes for a resource range on a PCI root bridge.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Attributes            The mask of attributes to set.
+  @param  ResourceBase          A pointer to the base address of the resource range to be modified by the
+                                attributes specified by Attributes.
+  @param  ResourceLength        A pointer to the length of the resource range to be modified by the
+                                attributes specified by Attributes.
+
+  @retval EFI_SUCCESS           The set of attributes specified by Attributes for the resource
+                                range specified by ResourceBase and ResourceLength
+                                were set on the PCI root bridge, and the actual resource range is
+                                returned in ResuourceBase and ResourceLength.
+  @retval EFI_UNSUPPORTED       A bit is set in Attributes that is not supported by the PCI Root
+                                Bridge.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the attributes on the
+                                resource range specified by BaseAddress and Length.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES)(
+  IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL              *This,
+  IN     UINT64                                   Attributes,
+  IN OUT UINT64                                   *ResourceBase,
+  IN OUT UINT64                                   *ResourceLength
+  );
+
+/**
+  Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI 2.0
+  resource descriptors.
+
+  @param  This                  A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
+  @param  Resources             A pointer to the ACPI 2.0 resource descriptors that describe the current
+                                configuration of this PCI root bridge.
+
+  @retval EFI_SUCCESS           The current configuration of this PCI root bridge was returned in
+                                Resources.
+  @retval EFI_UNSUPPORTED       The current configuration of this PCI root bridge could not be
+                                retrieved.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION)(
+  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL          *This,
+  OUT VOID                                     **Resources
+  );
+
+/**
+  @par Protocol Description:
+  Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are
+  used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller.
+
+  @param ParentHandle
+  The EFI_HANDLE of the PCI Host Bridge of which this PCI Root Bridge is a member.
+
+  @param PollMem
+  Polls an address in memory mapped I/O space until an exit condition is met,
+  or a timeout occurs.
+
+  @param PollIo
+  Polls an address in I/O space until an exit condition is met, or a timeout occurs.
+
+  @param Mem.Read
+  Allows reads from memory mapped I/O space.
+
+  @param Mem.Write
+  Allows writes to memory mapped I/O space.
+
+  @param Io.Read
+  Allows reads from I/O space.
+
+  @param Io.Write
+  Allows writes to I/O space.
+
+  @param Pci.Read
+  Allows reads from PCI configuration space.
+
+  @param Pci.Write
+  Allows writes to PCI configuration space.
+
+  @param CopyMem
+  Allows one region of PCI root bridge memory space to be copied to another
+  region of PCI root bridge memory space.
+
+  @param Map
+  Provides the PCI controller's specific addresses needed to access system memory for DMA.
+
+  @param Unmap
+  Releases any resources allocated by Map().
+
+  @param AllocateBuffer
+  Allocates pages that are suitable for a common buffer mapping.
+
+  @param FreeBuffer
+  Free pages that were allocated with AllocateBuffer().
+
+  @param Flush
+  Flushes all PCI posted write transactions to system memory.
+
+  @param GetAttributes
+  Gets the attributes that a PCI root bridge supports setting with SetAttributes(),
+  and the attributes that a PCI root bridge is currently using.
+
+  @param SetAttributes
+  Sets attributes for a resource range on a PCI root bridge.
+
+  @param Configuration
+  Gets the current resource settings for this PCI root bridge.
+
+  @param SegmentNumber
+  The segment number that this PCI root bridge resides.
+
+**/
+struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL {
+  EFI_HANDLE                                      ParentHandle;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM     PollMem;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM     PollIo;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS          Mem;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS          Io;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS          Pci;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM        CopyMem;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP             Map;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP           Unmap;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER     FreeBuffer;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH           Flush;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES  GetAttributes;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES  SetAttributes;
+  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION   Configuration;
+  UINT32                                          SegmentNumber;
+};
+
+extern EFI_GUID gEfiPciRootBridgeIoProtocolGuid;
+
+#endif
index 36aab5e..0643066 100644 (file)
 /* Reset any trailing #pragma pack directives */
 #pragma pack()
 
+#include <gpxe/tables.h>
+#include <gpxe/uuid.h>
+
+/** An EFI protocol used by gPXE */
+struct efi_protocol {
+       union {
+               /** EFI protocol GUID */
+               EFI_GUID guid;
+               /** UUID structure understood by gPXE */
+               union uuid uuid;
+       } u;
+       /** Variable containing pointer to protocol structure */
+       void **protocol;
+};
+
+/** Declare an EFI protocol used by gPXE */
+#define __efi_protocol \
+       __table ( struct efi_protocol, efi_protocols, 01 )
+
+/** Declare an EFI protocol to be required by gPXE
+ *
+ * @v _protocol                EFI protocol name
+ * @v _ptr             Pointer to protocol instance
+ */
+#define EFI_REQUIRE_PROTOCOL( _protocol, _ptr )                                     \
+       struct efi_protocol __ ## _protocol __efi_protocol = {               \
+               .u.guid = _protocol ## _GUID,                                \
+               .protocol = ( ( void ** ) ( void * )                         \
+                             ( ( (_ptr) == ( ( _protocol ** ) NULL ) ) ?    \
+                               (_ptr) : (_ptr) ) ),                         \
+       }
+
+/** Convert a gPXE status code to an EFI status code
+ *
+ * FIXME: actually perform some kind of conversion.  gPXE error codes
+ * will be detected as EFI error codes; both have the top bit set, and
+ * the success return code is zero for both.  Anything that just
+ * reports a numerical error will be OK, anything attempting to
+ * interpret the value or to display a text equivalent will be
+ * screwed.
+ */
+#define RC_TO_EFIRC( rc ) (rc)
+
+/** Convert an EFI status code to a gPXE status code
+ *
+ * FIXME: as above
+ */
+#define EFIRC_TO_RC( efirc ) (efirc)
+
+extern EFI_HANDLE efi_image_handle;
+extern EFI_SYSTEM_TABLE *efi_systab;
+
 #endif /* _EFI_H */
diff --git a/src/include/gpxe/efi/efi_io.h b/src/include/gpxe/efi/efi_io.h
new file mode 100644 (file)
index 0000000..93f559d
--- /dev/null
@@ -0,0 +1,178 @@
+#ifndef _GPXE_EFI_IO_H
+#define _GPXE_EFI_IO_H
+
+/** @file
+ *
+ * gPXE I/O API for EFI
+ *
+ * EFI runs with flat physical addressing, so the various mappings
+ * between virtual addresses, I/O addresses and bus addresses are all
+ * no-ops.  I/O is handled using the EFI_CPU_IO_PROTOCOL.
+ */
+
+#ifdef IOAPI_EFI
+#define IOAPI_PREFIX_efi
+#else
+#define IOAPI_PREFIX_efi __efi_
+#endif
+
+extern unsigned long long efi_ioread ( volatile void *io_addr,
+                                      size_t size );
+extern void efi_iowrite ( unsigned long long data, volatile void *io_addr,
+                         size_t size );
+extern void efi_ioreads ( volatile void *io_addr, void *data,
+                         size_t size, unsigned int count );
+extern void efi_iowrites ( volatile void *io_addr, const void *data,
+                         size_t size, unsigned int count );
+
+/*
+ * Physical<->Bus and Bus<->I/O address mappings
+ *
+ * EFI runs with flat physical addressing, so these are all no-ops.
+ *
+ */
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( efi, phys_to_bus ) ( unsigned long phys_addr ) {
+       return phys_addr;
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( efi, bus_to_phys ) ( unsigned long bus_addr ) {
+       return bus_addr;
+}
+
+static inline __always_inline void *
+IOAPI_INLINE ( efi, ioremap ) ( unsigned long bus_addr, size_t len __unused ) {
+       return ( ( void * ) bus_addr );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, iounmap ) ( volatile const void *io_addr __unused ) {
+       /* Nothing to do */
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( efi, io_to_bus ) ( volatile const void *io_addr ) {
+       return ( ( unsigned long ) io_addr );
+}
+
+/*
+ * I/O functions
+ *
+ */
+
+static inline __always_inline uint8_t
+IOAPI_INLINE ( efi, readb ) ( volatile uint8_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint16_t
+IOAPI_INLINE ( efi, readw ) ( volatile uint16_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint32_t
+IOAPI_INLINE ( efi, readl ) ( volatile uint32_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint64_t
+IOAPI_INLINE ( efi, readq ) ( volatile uint64_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, writeb ) ( uint8_t data, volatile uint8_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, writew ) ( uint16_t data, volatile uint16_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, writel ) ( uint32_t data, volatile uint32_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, writeq ) ( uint64_t data, volatile uint64_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint8_t
+IOAPI_INLINE ( efi, inb ) ( volatile uint8_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint16_t
+IOAPI_INLINE ( efi, inw ) ( volatile uint16_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline uint32_t
+IOAPI_INLINE ( efi, inl ) ( volatile uint32_t *io_addr ) {
+       return efi_ioread ( io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outb ) ( uint8_t data, volatile uint8_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outw ) ( uint16_t data, volatile uint16_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outl ) ( uint32_t data, volatile uint32_t *io_addr ) {
+       efi_iowrite ( data, io_addr, sizeof ( *io_addr ) );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, insb ) ( volatile uint8_t *io_addr, uint8_t *data,
+                            unsigned int count ) {
+       efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, insw ) ( volatile uint16_t *io_addr, uint16_t *data,
+                            unsigned int count ) {
+       efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, insl ) ( volatile uint32_t *io_addr, uint32_t *data,
+                            unsigned int count ) {
+       efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outsb ) ( volatile uint8_t *io_addr, const uint8_t *data,
+                             unsigned int count ) {
+       efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outsw ) ( volatile uint16_t *io_addr, const uint16_t *data,
+                             unsigned int count ) {
+       efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, outsl ) ( volatile uint32_t *io_addr, const uint32_t *data,
+                             unsigned int count ) {
+       efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count );
+}
+
+static inline __always_inline void
+IOAPI_INLINE ( efi, mb ) ( void ) {
+       /* Do nothing; EFI readl()/writel() calls already act as
+        * memory barriers.
+        */
+}
+
+#endif /* _GPXE_EFI_IO_H */
diff --git a/src/include/gpxe/efi/efi_pci.h b/src/include/gpxe/efi/efi_pci.h
new file mode 100644 (file)
index 0000000..8be331a
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef _GPXE_EFI_PCI_H
+#define _GPXE_EFI_PCI_H
+
+/** @file
+ *
+ * gPXE PCI I/O API for EFI
+ *
+ */
+
+#ifdef PCIAPI_EFI
+#define PCIAPI_PREFIX_efi
+#else
+#define PCIAPI_PREFIX_efi __efi_
+#endif
+
+/* EFI PCI width codes defined by EFI spec */
+#define EFIPCI_WIDTH_BYTE 0
+#define EFIPCI_WIDTH_WORD 1
+#define EFIPCI_WIDTH_DWORD 2
+
+#define EFIPCI_LOCATION( _offset, _width ) \
+       ( (_offset) | ( (_width) << 16 ) )
+#define EFIPCI_OFFSET( _location ) ( (_location) & 0xffff )
+#define EFIPCI_WIDTH( _location ) ( (_location) >> 16 )
+
+struct pci_device;
+
+extern int efipci_read ( struct pci_device *pci, unsigned long location,
+                        void *value );
+extern int efipci_write ( struct pci_device *pci, unsigned long location,
+                         unsigned long value );
+
+/**
+ * Determine maximum PCI bus number within system
+ *
+ * @ret max_bus                Maximum bus number
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_max_bus ) ( void ) {
+       /* No way to work this out via EFI */
+       return 0xff;
+}
+
+/**
+ * Read byte from PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value read
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_read_config_byte ) ( struct pci_device *pci,
+                                             unsigned int where,
+                                             uint8_t *value ) {
+       return efipci_read ( pci,
+                            EFIPCI_LOCATION ( where, EFIPCI_WIDTH_BYTE ),
+                            value );
+}
+
+/**
+ * Read word from PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value read
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_read_config_word ) ( struct pci_device *pci,
+                                             unsigned int where,
+                                             uint16_t *value ) {
+       return efipci_read ( pci,
+                            EFIPCI_LOCATION ( where, EFIPCI_WIDTH_WORD ),
+                            value );
+}
+
+/**
+ * Read dword from PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value read
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_read_config_dword ) ( struct pci_device *pci,
+                                              unsigned int where,
+                                              uint32_t *value ) {
+       return efipci_read ( pci,
+                            EFIPCI_LOCATION ( where, EFIPCI_WIDTH_DWORD ),
+                            value );
+}
+
+/**
+ * Write byte to PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value to be written
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_write_config_byte ) ( struct pci_device *pci,
+                                              unsigned int where,
+                                              uint8_t value ) {
+       return efipci_write ( pci,
+                             EFIPCI_LOCATION ( where, EFIPCI_WIDTH_BYTE ),
+                             value );
+}
+
+/**
+ * Write word to PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value to be written
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_write_config_word ) ( struct pci_device *pci,
+                                              unsigned int where,
+                                              uint16_t value ) {
+       return efipci_write ( pci,
+                             EFIPCI_LOCATION ( where, EFIPCI_WIDTH_WORD ),
+                             value );
+}
+
+/**
+ * Write dword to PCI configuration space via EFI
+ *
+ * @v pci      PCI device
+ * @v where    Location within PCI configuration space
+ * @v value    Value to be written
+ * @ret rc     Return status code
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( efi, pci_write_config_dword ) ( struct pci_device *pci,
+                                               unsigned int where,
+                                               uint32_t value ) {
+       return efipci_write ( pci,
+                             EFIPCI_LOCATION ( where, EFIPCI_WIDTH_DWORD ),
+                             value );
+}
+
+#endif /* _GPXE_EFI_PCI_H */
diff --git a/src/include/gpxe/efi/efi_timer.h b/src/include/gpxe/efi/efi_timer.h
new file mode 100644 (file)
index 0000000..c332c9d
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _GPXE_EFI_TIMER_H
+#define _GPXE_EFI_TIMER_H
+
+/** @file
+ *
+ * gPXE timer API for EFI
+ *
+ */
+
+#ifdef TIMER_EFI
+#define TIMER_PREFIX_efi
+#else
+#define TIMER_PREFIX_efi __efi_
+#endif
+
+#endif /* _GPXE_EFI_TIMER_H */
diff --git a/src/include/gpxe/efi/efi_uaccess.h b/src/include/gpxe/efi/efi_uaccess.h
new file mode 100644 (file)
index 0000000..bae5fb4
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef _GPXE_EFI_UACCESS_H
+#define _GPXE_EFI_UACCESS_H
+
+/** @file
+ *
+ * gPXE user access API for EFI
+ *
+ * EFI runs with flat physical addressing, so the various mappings
+ * between virtual addresses, I/O addresses and bus addresses are all
+ * no-ops.
+ */
+
+#ifdef UACCESS_EFI
+#define UACCESS_PREFIX_efi
+#else
+#define UACCESS_PREFIX_efi __efi_
+#endif
+
+/**
+ * Convert physical address to user pointer
+ *
+ * @v phys_addr                Physical address
+ * @ret userptr                User pointer
+ */
+static inline __always_inline userptr_t
+UACCESS_INLINE ( efi, phys_to_user ) ( unsigned long phys_addr ) {
+       return phys_addr;
+}
+
+/**
+ * Convert user buffer to physical address
+ *
+ * @v userptr          User pointer
+ * @v offset           Offset from user pointer
+ * @ret phys_addr      Physical address
+ */
+static inline __always_inline unsigned long
+UACCESS_INLINE ( efi, user_to_phys ) ( userptr_t userptr, off_t offset ) {
+       return ( userptr + offset );
+}
+
+static inline __always_inline userptr_t
+UACCESS_INLINE ( efi, virt_to_user ) ( volatile const void *addr ) {
+       return trivial_virt_to_user ( addr );
+}
+
+static inline __always_inline void *
+UACCESS_INLINE ( efi, user_to_virt ) ( userptr_t userptr, off_t offset ) {
+       return trivial_user_to_virt ( userptr, offset );
+}
+
+static inline __always_inline userptr_t
+UACCESS_INLINE ( efi, userptr_add ) ( userptr_t userptr, off_t offset ) {
+       return trivial_userptr_add ( userptr, offset );
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( efi, memcpy_user ) ( userptr_t dest, off_t dest_off,
+                                       userptr_t src, off_t src_off,
+                                       size_t len ) {
+       trivial_memcpy_user ( dest, dest_off, src, src_off, len );
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( efi, memmove_user ) ( userptr_t dest, off_t dest_off,
+                                        userptr_t src, off_t src_off,
+                                        size_t len ) {
+       trivial_memmove_user ( dest, dest_off, src, src_off, len );
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( efi, memset_user ) ( userptr_t buffer, off_t offset,
+                                       int c, size_t len ) {
+       trivial_memset_user ( buffer, offset, c, len );
+}
+
+static inline __always_inline size_t
+UACCESS_INLINE ( efi, strlen_user ) ( userptr_t buffer, off_t offset ) {
+       return trivial_strlen_user ( buffer, offset );
+}
+
+static inline __always_inline off_t
+UACCESS_INLINE ( efi, memchr_user ) ( userptr_t buffer, off_t offset,
+                                       int c, size_t len ) {
+       return trivial_memchr_user ( buffer, offset, c, len );
+}
+
+#endif /* _GPXE_EFI_UACCESS_H */
diff --git a/src/include/gpxe/efi/efi_umalloc.h b/src/include/gpxe/efi/efi_umalloc.h
new file mode 100644 (file)
index 0000000..def17b2
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _GPXE_EFI_UMALLOC_H
+#define _GPXE_EFI_UMALLOC_H
+
+/** @file
+ *
+ * gPXE user memory allocation API for EFI
+ *
+ */
+
+#ifdef UMALLOC_EFI
+#define UMALLOC_PREFIX_efi
+#else
+#define UMALLOC_PREFIX_efi __efi_
+#endif
+
+#endif /* _GPXE_EFI_UMALLOC_H */
index ca0abeb..609500c 100644 (file)
 #define ERRFILE_elf                  ( ERRFILE_IMAGE | 0x00010000 )
 #define ERRFILE_script               ( ERRFILE_IMAGE | 0x00020000 )
 #define ERRFILE_segment                      ( ERRFILE_IMAGE | 0x00030000 )
+#define ERRFILE_efi_image            ( ERRFILE_IMAGE | 0x00040000 )
 
 #define ERRFILE_asn1                 ( ERRFILE_OTHER | 0x00000000 )
 #define ERRFILE_chap                 ( ERRFILE_OTHER | 0x00010000 )
 #define ERRFILE_tls                  ( ERRFILE_OTHER | 0x000d0000 )
 #define ERRFILE_ifmgmt               ( ERRFILE_OTHER | 0x000e0000 )
 #define ERRFILE_iscsiboot            ( ERRFILE_OTHER | 0x000f0000 )
+#define ERRFILE_efi_pci                      ( ERRFILE_OTHER | 0x00100000 )
 
 /** @} */
 
index 107ff08..e4d2921 100644 (file)
@@ -46,6 +46,7 @@
 #define DHCP_EB_FEATURE_PXE            0x21 /**< PXE format */
 #define DHCP_EB_FEATURE_ELF            0x22 /**< ELF format */
 #define DHCP_EB_FEATURE_COMBOOT                0x23 /**< COMBOOT format */
+#define DHCP_EB_FEATURE_EFI            0x24 /**< EFI format */
 
 /** @} */
 
index 0fc7c74..ebb8ba3 100644 (file)
@@ -51,6 +51,7 @@
        PROVIDE_SINGLE_API_INLINE ( IOAPI_PREFIX_ ## _subsys, _api_func )
 
 /* Include all architecture-independent I/O API headers */
+#include <gpxe/efi/efi_io.h>
 
 /* Include all architecture-dependent I/O API headers */
 #include <bits/io.h>
index db50939..365166c 100644 (file)
@@ -41,6 +41,7 @@
        PROVIDE_SINGLE_API_INLINE ( PCIAPI_PREFIX_ ## _subsys, _api_func )
 
 /* Include all architecture-independent I/O API headers */
+#include <gpxe/efi/efi_pci.h>
 
 /* Include all architecture-dependent I/O API headers */
 #include <bits/pci_io.h>
index e62007a..862d87b 100644 (file)
@@ -42,6 +42,7 @@
        PROVIDE_SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func )
 
 /* Include all architecture-independent I/O API headers */
+#include <gpxe/efi/efi_timer.h>
 
 /* Include all architecture-dependent I/O API headers */
 #include <bits/timer.h>
index f677b7f..33aaed1 100644 (file)
@@ -186,6 +186,7 @@ trivial_memchr_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
        PROVIDE_SINGLE_API_INLINE ( UACCESS_PREFIX_ ## _subsys, _api_func )
 
 /* Include all architecture-independent user access API headers */
+#include <gpxe/efi/efi_uaccess.h>
 
 /* Include all architecture-dependent user access API headers */
 #include <bits/uaccess.h>
index ffa0c0c..e6fc7bf 100644 (file)
@@ -23,6 +23,7 @@
        PROVIDE_SINGLE_API ( UMALLOC_PREFIX_ ## _subsys, _api_func, _func )
 
 /* Include all architecture-independent I/O API headers */
+#include <gpxe/efi/efi_umalloc.h>
 
 /* Include all architecture-dependent I/O API headers */
 #include <bits/umalloc.h>
diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c
new file mode 100644 (file)
index 0000000..b6e0daf
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/ansiesc.h>
+#include <console.h>
+
+#define ATTR_BOLD              0x08
+
+#define ATTR_FCOL_MASK         0x07
+#define ATTR_FCOL_BLACK                0x00
+#define ATTR_FCOL_BLUE         0x01
+#define ATTR_FCOL_GREEN                0x02
+#define ATTR_FCOL_CYAN         0x03
+#define ATTR_FCOL_RED          0x04
+#define ATTR_FCOL_MAGENTA      0x05
+#define ATTR_FCOL_YELLOW       0x06
+#define ATTR_FCOL_WHITE                0x07
+
+#define ATTR_BCOL_MASK         0x70
+#define ATTR_BCOL_BLACK                0x00
+#define ATTR_BCOL_BLUE         0x10
+#define ATTR_BCOL_GREEN                0x20
+#define ATTR_BCOL_CYAN         0x30
+#define ATTR_BCOL_RED          0x40
+#define ATTR_BCOL_MAGENTA      0x50
+#define ATTR_BCOL_YELLOW       0x60
+#define ATTR_BCOL_WHITE                0x70
+
+#define ATTR_DEFAULT           ATTR_FCOL_WHITE
+
+/** Current character attribute */
+static unsigned int efi_attr = ATTR_DEFAULT;
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v count            Parameter count
+ * @v params[0]                Row (1 is top)
+ * @v params[1]                Column (1 is left)
+ */
+static void efi_handle_cup ( unsigned int count __unused, int params[] ) {
+       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+       int cx = ( params[1] - 1 );
+       int cy = ( params[0] - 1 );
+
+       if ( cx < 0 )
+               cx = 0;
+       if ( cy < 0 )
+               cy = 0;
+
+       conout->SetCursorPosition ( conout, cx, cy );
+}
+
+/**
+ * Handle ANSI ED (erase in page)
+ *
+ * @v count            Parameter count
+ * @v params[0]                Region to erase
+ */
+static void efi_handle_ed ( unsigned int count __unused,
+                            int params[] __unused ) {
+       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+
+       /* We assume that we always clear the whole screen */
+       assert ( params[0] == ANSIESC_ED_ALL );
+
+       conout->ClearScreen ( conout );
+}
+
+/**
+ * Handle ANSI SGR (set graphics rendition)
+ *
+ * @v count            Parameter count
+ * @v params           List of graphic rendition aspects
+ */
+static void efi_handle_sgr ( unsigned int count, int params[] ) {
+       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+       static const uint8_t efi_attr_fcols[10] = {
+               ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
+               ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
+               ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
+               ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
+       };
+       static const uint8_t efi_attr_bcols[10] = {
+               ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
+               ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
+               ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
+               ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
+       };
+       unsigned int i;
+       int aspect;
+
+       for ( i = 0 ; i < count ; i++ ) {
+               aspect = params[i];
+               if ( aspect == 0 ) {
+                       efi_attr = ATTR_DEFAULT;
+               } else if ( aspect == 1 ) {
+                       efi_attr |= ATTR_BOLD;
+               } else if ( aspect == 22 ) {
+                       efi_attr &= ~ATTR_BOLD;
+               } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
+                       efi_attr &= ~ATTR_FCOL_MASK;
+                       efi_attr |= efi_attr_fcols[ aspect - 30 ];
+               } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
+                       efi_attr &= ~ATTR_BCOL_MASK;
+                       efi_attr |= efi_attr_bcols[ aspect - 40 ];
+               }
+       }
+
+       conout->SetAttribute ( conout, efi_attr );
+}
+
+/** EFI console ANSI escape sequence handlers */
+static struct ansiesc_handler efi_ansiesc_handlers[] = {
+       { ANSIESC_CUP, efi_handle_cup },
+       { ANSIESC_ED, efi_handle_ed },
+       { ANSIESC_SGR, efi_handle_sgr },
+       { 0, NULL }
+};
+
+/** EFI console ANSI escape sequence context */
+static struct ansiesc_context efi_ansiesc_ctx = {
+       .handlers = efi_ansiesc_handlers,
+};
+
+/**
+ * Print a character to EFI console
+ *
+ * @v character                Character to be printed
+ */
+static void efi_putchar ( int character ) {
+       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+       wchar_t wstr[] = { character, 0 };
+
+       /* Intercept ANSI escape sequences */
+       character = ansiesc_process ( &efi_ansiesc_ctx, character );
+       if ( character < 0 )
+               return;
+
+       conout->OutputString ( conout, wstr );
+}
+
+/**
+ * Pointer to current ANSI output sequence
+ *
+ * While we are in the middle of returning an ANSI sequence for a
+ * special key, this will point to the next character to return.  When
+ * not in the middle of such a sequence, this will point to a NUL
+ * (note: not "will be NULL").
+ */
+static const char *ansi_input = "";
+
+/** Mapping from EFI scan codes to ANSI escape sequences */
+static const char *ansi_sequences[] = {
+       [SCAN_UP] = "[A",
+       [SCAN_DOWN] = "[B",
+       [SCAN_RIGHT] = "[C",
+       [SCAN_LEFT] = "[D",
+       [SCAN_HOME] = "[H",
+       [SCAN_END] = "[F",
+       [SCAN_INSERT] = "[2~",
+       /* EFI translates an incoming backspace via the serial console
+        * into a SCAN_DELETE.  There's not much we can do about this.
+        */
+       [SCAN_DELETE] = "[3~",
+       [SCAN_PAGE_UP] = "[5~",
+       [SCAN_PAGE_DOWN] = "[6~",
+       /* EFI translates some (but not all) incoming escape sequences
+        * via the serial console into equivalent scancodes.  When it
+        * doesn't recognise a sequence, it helpfully(!) translates
+        * the initial ESC and passes the remainder through verbatim.
+        * Treating SCAN_ESC as equivalent to an empty escape sequence
+        * works around this bug.
+        */
+       [SCAN_ESC] = "",
+};
+
+/**
+ * Get ANSI escape sequence corresponding to EFI scancode
+ *
+ * @v scancode         EFI scancode
+ * @ret ansi_seq       ANSI escape sequence, if any, otherwise NULL
+ */
+static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
+       if ( scancode < ( sizeof ( ansi_sequences ) /
+                         sizeof ( ansi_sequences[0] ) ) ) {
+               return ansi_sequences[scancode];
+       }
+       return NULL;
+}
+
+/**
+ * Get character from EFI console
+ *
+ * @ret character      Character read from console
+ */
+static int efi_getchar ( void ) {
+       EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+       const char *ansi_seq;
+       EFI_INPUT_KEY key;
+       EFI_STATUS efirc;
+
+       /* If we are mid-sequence, pass out the next byte */
+       if ( *ansi_input )
+               return *(ansi_input++);
+
+       /* Read key from real EFI console */
+       if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) {
+               DBG ( "EFI could not read keystroke: %lx\n", efirc );
+               return 0;
+       }
+       DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
+              key.UnicodeChar, key.ScanCode );
+
+       /* If key has a Unicode representation, return it */
+       if ( key.UnicodeChar )
+               return key.UnicodeChar;
+
+       /* Otherwise, check for a special key that we know about */
+       if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) {
+               /* Start of escape sequence: return ESC (0x1b) */
+               ansi_input = ansi_seq;
+               return 0x1b;
+       }
+
+       return 0;
+}
+
+/**
+ * Check for character ready to read from EFI console
+ *
+ * @ret True           Character available to read
+ * @ret False          No character available to read
+ */
+static int efi_iskey ( void ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+       EFI_STATUS efirc;
+
+       /* If we are mid-sequence, we are always ready */
+       if ( *ansi_input )
+               return 1;
+
+       /* Check to see if the WaitForKey event has fired */
+       if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
+               return 1;
+
+       return 0;
+}
+
+struct console_driver efi_console __console_driver = {
+       .putchar = efi_putchar,
+       .getchar = efi_getchar,
+       .iskey = efi_iskey,
+};
diff --git a/src/interface/efi/efi_entry.c b/src/interface/efi/efi_entry.c
new file mode 100644 (file)
index 0000000..b00828a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/uuid.h>
+
+/** Image handle passed to entry point */
+EFI_HANDLE efi_image_handle;
+
+/** System table passed to entry point */
+EFI_SYSTEM_TABLE *efi_systab;
+
+/** Declared used EFI protocols */
+static struct efi_protocol efi_protocols[0] \
+       __table_start ( struct efi_protocol, efi_protocols );
+static struct efi_protocol efi_protocols_end[0] \
+       __table_end ( struct efi_protocol, efi_protocols );
+
+/**
+ * EFI entry point
+ *
+ * @v image_handle     Image handle
+ * @v systab           System table
+ * @ret efirc          EFI return status code
+ */
+EFI_STATUS EFIAPI efi_entry ( EFI_HANDLE image_handle,
+                             EFI_SYSTEM_TABLE *systab ) {
+       EFI_BOOT_SERVICES *bs;
+       struct efi_protocol *prot;
+       EFI_STATUS efirc;
+
+       /* Store image handle and system table pointer for future use */
+       efi_image_handle = image_handle;
+       efi_systab = systab;
+
+       /* Sanity checks */
+       if ( ! systab )
+               return EFI_NOT_AVAILABLE_YET;
+       if ( ! systab->ConOut )
+               return EFI_NOT_AVAILABLE_YET;
+       if ( ! systab->BootServices ) {
+               DBGC ( systab, "EFI provided no BootServices entry point\n" );
+               return EFI_NOT_AVAILABLE_YET;
+       }
+       if ( ! systab->RuntimeServices ) {
+               DBGC ( systab, "EFI provided no RuntimeServices entry "
+                      "point\n" );
+               return EFI_NOT_AVAILABLE_YET;
+       }
+       DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
+
+       /* Look up required protocols */
+       bs = systab->BootServices;
+       for ( prot = efi_protocols ; prot < efi_protocols_end ; prot++ ) {
+               if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL,
+                                                   prot->protocol ) ) != 0 ) {
+                       DBGC ( systab, "EFI does not provide protocol %s\n",
+                              uuid_ntoa ( &prot->u.uuid ) );
+                       return efirc;
+               }
+               DBGC ( systab, "EFI protocol %s is at %p\n",
+                      uuid_ntoa ( &prot->u.uuid ), *(prot->protocol) );
+       }
+
+       /* Call to main() */
+       return RC_TO_EFIRC ( main () );
+}
diff --git a/src/interface/efi/efi_io.c b/src/interface/efi/efi_io.c
new file mode 100644 (file)
index 0000000..1d4537a
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/io.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/CpuIo.h>
+#include <gpxe/efi/efi_io.h>
+
+/** @file
+ *
+ * gPXE I/O API for EFI
+ *
+ */
+
+/** CPU I/O protocol */
+static EFI_CPU_IO_PROTOCOL *cpu_io;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io );
+
+/** Maximum address that can be used for port I/O */
+#define MAX_PORT_ADDRESS 0xffff
+
+/**
+ * Determine whether or not address is a port I/O address
+ *
+ * @v io_addr          I/O address
+ * @v is_port          I/O address is a port I/O address
+ */
+#define IS_PORT_ADDRESS(io_addr) \
+       ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS )
+
+/**
+ * Determine EFI CPU I/O width code
+ *
+ * @v size             Size of value
+ * @ret width          EFI width code
+ *
+ * Someone at Intel clearly gets paid by the number of lines of code
+ * they write.  No-one should ever be able to make I/O this
+ * convoluted.  The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite
+ * idiocy.
+ */
+static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) {
+       switch ( size ) {
+       case 1 :        return EfiCpuIoWidthFifoUint8;
+       case 2 :        return EfiCpuIoWidthFifoUint16;
+       case 4 :        return EfiCpuIoWidthFifoUint32;
+       case 8 :        return EfiCpuIoWidthFifoUint64;
+       default :
+               assert ( 0 );
+               /* I wonder what this will actually do... */
+               return EfiCpuIoWidthMaximum;
+       }
+}
+
+/**
+ * Read from device
+ *
+ * @v io_addr          I/O address
+ * @v size             Size of value
+ * @ret data           Value read
+ */
+unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) {
+       EFI_CPU_IO_PROTOCOL_IO_MEM read;
+       unsigned long long data = 0;
+       EFI_STATUS efirc;
+
+       read = ( IS_PORT_ADDRESS ( io_addr ) ?
+                cpu_io->Io.Read : cpu_io->Mem.Read );
+
+       if ( ( efirc = read ( cpu_io, efi_width ( size ),
+                             ( intptr_t ) io_addr, 1,
+                             ( void * ) &data ) ) != 0 ) {
+               DBG ( "EFI I/O read at %p failed: %lx\n", io_addr, efirc );
+               return -1ULL;
+       }
+
+       return data;
+}
+
+/**
+ * Write to device
+ *
+ * @v data             Value to write
+ * @v io_addr          I/O address
+ * @v size             Size of value
+ */
+void efi_iowrite ( unsigned long long data, volatile void *io_addr,
+                  size_t size ) {
+       EFI_CPU_IO_PROTOCOL_IO_MEM write;
+       EFI_STATUS efirc;
+
+       write = ( IS_PORT_ADDRESS ( io_addr ) ?
+                 cpu_io->Io.Write : cpu_io->Mem.Write );
+
+       if ( ( efirc = write ( cpu_io, efi_width ( size ),
+                              ( intptr_t ) io_addr, 1,
+                              ( void * ) &data ) ) != 0 ) {
+               DBG ( "EFI I/O write at %p failed: %lx\n", io_addr, efirc );
+       }
+}
+
+/**
+ * String read from device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v size             Size of values
+ * @v count            Number of values to read
+ */
+void efi_ioreads ( volatile void *io_addr, void *data,
+                  size_t size, unsigned int count ) {
+       EFI_CPU_IO_PROTOCOL_IO_MEM read;
+       EFI_STATUS efirc;
+
+       read = ( IS_PORT_ADDRESS ( io_addr ) ?
+                cpu_io->Io.Read : cpu_io->Mem.Read );
+
+       if ( ( efirc = read ( cpu_io, efi_width ( size ),
+                             ( intptr_t ) io_addr, count,
+                             ( void * ) data ) ) != 0 ) {
+               DBG ( "EFI I/O string read at %p failed: %lx\n",
+                     io_addr, efirc );
+       }
+}
+
+/**
+ * String write to device
+ *
+ * @v io_addr          I/O address
+ * @v data             Data buffer
+ * @v size             Size of values
+ * @v count            Number of values to write
+ */
+void efi_iowrites ( volatile void *io_addr, const void *data,
+                   size_t size, unsigned int count ) {
+       EFI_CPU_IO_PROTOCOL_IO_MEM write;
+       EFI_STATUS efirc;
+
+       write = ( IS_PORT_ADDRESS ( io_addr ) ?
+                cpu_io->Io.Write : cpu_io->Mem.Write );
+
+       if ( ( efirc = write ( cpu_io, efi_width ( size ),
+                              ( intptr_t ) io_addr, count,
+                              ( void * ) data ) ) != 0 ) {
+               DBG ( "EFI I/O write at %p failed: %lx\n",
+                     io_addr, efirc );
+       }
+}
+
+/**
+ * Wait for I/O-mapped operation to complete
+ *
+ */
+static void efi_iodelay ( void ) {
+       /* Write to non-existent port.  Probably x86-only. */
+       outb ( 0, 0x80 );
+}
+
+PROVIDE_IOAPI_INLINE ( efi, phys_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, bus_to_phys );
+PROVIDE_IOAPI_INLINE ( efi, ioremap );
+PROVIDE_IOAPI_INLINE ( efi, iounmap );
+PROVIDE_IOAPI_INLINE ( efi, io_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, readb );
+PROVIDE_IOAPI_INLINE ( efi, readw );
+PROVIDE_IOAPI_INLINE ( efi, readl );
+PROVIDE_IOAPI_INLINE ( efi, readq );
+PROVIDE_IOAPI_INLINE ( efi, writeb );
+PROVIDE_IOAPI_INLINE ( efi, writew );
+PROVIDE_IOAPI_INLINE ( efi, writel );
+PROVIDE_IOAPI_INLINE ( efi, writeq );
+PROVIDE_IOAPI_INLINE ( efi, inb );
+PROVIDE_IOAPI_INLINE ( efi, inw );
+PROVIDE_IOAPI_INLINE ( efi, inl );
+PROVIDE_IOAPI_INLINE ( efi, outb );
+PROVIDE_IOAPI_INLINE ( efi, outw );
+PROVIDE_IOAPI_INLINE ( efi, outl );
+PROVIDE_IOAPI_INLINE ( efi, insb );
+PROVIDE_IOAPI_INLINE ( efi, insw );
+PROVIDE_IOAPI_INLINE ( efi, insl );
+PROVIDE_IOAPI_INLINE ( efi, outsb );
+PROVIDE_IOAPI_INLINE ( efi, outsw );
+PROVIDE_IOAPI_INLINE ( efi, outsl );
+PROVIDE_IOAPI ( efi, iodelay, efi_iodelay );
+PROVIDE_IOAPI_INLINE ( efi, mb );
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
new file mode 100644 (file)
index 0000000..9340816
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/PciRootBridgeIo.h>
+
+/** @file
+ *
+ * gPXE PCI I/O API for EFI
+ *
+ */
+
+/** PCI root bridge I/O protocol */
+static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci;
+EFI_REQUIRE_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci );
+
+static unsigned long efipci_address ( struct pci_device *pci,
+                                     unsigned long location ) {
+       return EFI_PCI_ADDRESS ( pci->bus, PCI_SLOT ( pci->devfn ),
+                                PCI_FUNC ( pci->devfn ),
+                                EFIPCI_OFFSET ( location ) );
+}
+
+int efipci_read ( struct pci_device *pci, unsigned long location,
+                 void *value ) {
+       EFI_STATUS efirc;
+
+       if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ),
+                                         efipci_address ( pci, location ), 1,
+                                         value ) ) != 0 ) {
+               DBG ( "EFIPCI config read from %02x:%02x.%x offset %02lx "
+                     "failed: %lx\n", pci->bus, PCI_SLOT ( pci->devfn ),
+                     PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+                     efirc );
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int efipci_write ( struct pci_device *pci, unsigned long location,
+                  unsigned long value ) {
+       EFI_STATUS efirc;
+
+       if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ),
+                                          efipci_address ( pci, location ), 1,
+                                          &value ) ) != 0 ) {
+               DBG ( "EFIPCI config write to %02x:%02x.%x offset %02lx "
+                     "failed: %lx\n", pci->bus, PCI_SLOT ( pci->devfn ),
+                     PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+                     efirc );
+               return -EIO;
+       }
+
+       return 0;
+}
+
+PROVIDE_PCIAPI_INLINE ( efi, pci_max_bus );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c
new file mode 100644 (file)
index 0000000..b4e54a6
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <limits.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/timer.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/Cpu.h>
+
+/** @file
+ *
+ * gPXE timer API for EFI
+ *
+ */
+
+/** Scale factor to apply to CPU timer 0
+ *
+ * The timer is scaled down in order to ensure that reasonable values
+ * for "number of ticks" don't exceed the size of an unsigned long.
+ */
+#define EFI_TIMER0_SHIFT 12
+
+/** Calibration time */
+#define EFI_CALIBRATE_DELAY_MS 1
+
+/** CPU protocol */
+static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs            Number of microseconds for which to delay
+ */
+static void efi_udelay ( unsigned long usecs ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_STATUS efirc;
+
+       if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
+               DBG ( "EFI could not delay for %ldus: %lx\n",
+                     usecs, efirc );
+               /* Probably screwed */
+       }
+}
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks          Current time, in ticks
+ */
+static unsigned long efi_currticks ( void ) {
+       UINT64 time;
+       EFI_STATUS efirc;
+
+       /* Read CPU timer 0 (TSC) */
+       if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
+                                                NULL ) ) != 0 ) {
+               DBG ( "EFI could not read CPU timer: %lx\n", efirc );
+               /* Probably screwed */
+               return -1UL;
+       }
+
+       return ( time >> EFI_TIMER0_SHIFT );
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec  Number of ticks per second
+ */
+static unsigned long efi_ticks_per_sec ( void ) {
+       static unsigned long ticks_per_sec = 0;
+
+       /* Calibrate timer, if necessary.  EFI does nominally provide
+        * the timer speed via the (optional) TimerPeriod parameter to
+        * the GetTimerValue() call, but it gets the speed slightly
+        * wrong.  By up to three orders of magnitude.  Not helpful.
+        */
+       if ( ! ticks_per_sec ) {
+               unsigned long start;
+               unsigned long elapsed;
+
+               DBG ( "Calibrating EFI timer with a %d ms delay\n",
+                     EFI_CALIBRATE_DELAY_MS );
+               start = currticks();
+               mdelay ( EFI_CALIBRATE_DELAY_MS );
+               elapsed = ( currticks() - start );
+               ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
+               DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
+                     "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
+                     ticks_per_sec );
+       }
+
+       return ticks_per_sec;
+}
+
+PROVIDE_TIMER ( efi, udelay, efi_udelay );
+PROVIDE_TIMER ( efi, currticks, efi_currticks );
+PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
diff --git a/src/interface/efi/efi_uaccess.c b/src/interface/efi/efi_uaccess.c
new file mode 100644 (file)
index 0000000..1c54c03
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/uaccess.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE user access API for EFI
+ *
+ */
+
+PROVIDE_UACCESS_INLINE ( efi, phys_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_phys );
+PROVIDE_UACCESS_INLINE ( efi, virt_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_virt );
+PROVIDE_UACCESS_INLINE ( efi, userptr_add );
+PROVIDE_UACCESS_INLINE ( efi, memcpy_user );
+PROVIDE_UACCESS_INLINE ( efi, memmove_user );
+PROVIDE_UACCESS_INLINE ( efi, memset_user );
+PROVIDE_UACCESS_INLINE ( efi, strlen_user );
+PROVIDE_UACCESS_INLINE ( efi, memchr_user );
diff --git a/src/interface/efi/efi_umalloc.c b/src/interface/efi/efi_umalloc.c
new file mode 100644 (file)
index 0000000..d7357a1
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE user memory allocation API for EFI
+ *
+ */
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr          Memory previously allocated by umalloc(), or UNULL
+ * @v new_size         Requested size
+ * @ret new_ptr                Allocated memory, or UNULL
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_PHYSICAL_ADDRESS phys_addr;
+       unsigned int new_pages, old_pages;
+       userptr_t new_ptr = UNOWHERE;
+       size_t old_size;
+       EFI_STATUS efirc;
+
+       /* Allocate new memory if necessary.  If allocation fails,
+        * return without touching the old block.
+        */
+       if ( new_size ) {
+               new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 );
+               if ( ( efirc = bs->AllocatePages ( AllocateAnyPages,
+                                                  EfiBootServicesData,
+                                                  new_pages,
+                                                  &phys_addr ) ) != 0 ) {
+                       DBG ( "EFI could not allocate %d pages: %lx\n",
+                             new_pages, efirc );
+                       return UNULL;
+               }
+               assert ( phys_addr != 0 );
+               new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE );
+               copy_to_user ( new_ptr, -EFI_PAGE_SIZE,
+                              &new_size, sizeof ( new_size ) );
+               DBG ( "EFI allocated %d pages at %llx\n",
+                     new_pages, phys_addr );
+       }
+
+       /* Copy across relevant part of the old data region (if any),
+        * then free it.  Note that at this point either (a) new_ptr
+        * is valid, or (b) new_size is 0; either way, the memcpy() is
+        * valid.
+        */
+       if ( old_ptr && ( old_ptr != UNOWHERE ) ) {
+               copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE,
+                                sizeof ( old_size ) );
+               memcpy_user ( new_ptr, 0, old_ptr, 0,
+                             ( (old_size < new_size) ? old_size : new_size ));
+               old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 );
+               phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE );
+               if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){
+                       DBG ( "EFI could not free %d pages at %llx: %lx\n",
+                             old_pages, phys_addr, efirc );
+                       /* Not fatal; we have leaked memory but successfully
+                        * allocated (if asked to do so).
+                        */
+               }
+               DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr );
+       }
+
+       return new_ptr;
+}
+
+PROVIDE_UMALLOC ( efi, urealloc, efi_urealloc );
index 98adc2d..7f9d755 100644 (file)
@@ -2,3 +2,4 @@ nrv2b
 zbin
 hijack
 prototester
+efilink
diff --git a/src/util/efilink.c b/src/util/efilink.c
new file mode 100644 (file)
index 0000000..e21f4a9
--- /dev/null
@@ -0,0 +1,507 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <bfd.h>
+
+struct bfd_file {
+       bfd *bfd;
+       asymbol **symtab;
+       long symcount;
+};
+
+struct pe_relocs {
+       struct pe_relocs *next;
+       unsigned long start_rva;
+       unsigned int used_relocs;
+       unsigned int total_relocs;
+       uint16_t *relocs;
+};
+
+/**
+ * Allocate memory
+ *
+ * @v len              Length of memory to allocate
+ * @ret ptr            Pointer to allocated memory
+ */
+static void * xmalloc ( size_t len ) {
+       void *ptr;
+
+       ptr = malloc ( len );
+       if ( ! ptr ) {
+               fprintf ( stderr, "Could not allocate %zd bytes\n", len );
+               exit ( 1 );
+       }
+
+       return ptr;
+}
+
+/**
+ * Generate entry in PE relocation table
+ *
+ * @v pe_reltab                PE relocation table
+ * @v rva              RVA
+ * @v size             Size of relocation entry
+ */
+static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
+                               unsigned long rva, size_t size ) {
+       unsigned long start_rva;
+       uint16_t reloc;
+       struct pe_relocs *pe_rel;
+       uint16_t *relocs;
+
+       /* Construct */
+       start_rva = ( rva & ~0xfff );
+       reloc = ( rva & 0xfff );
+       switch ( size ) {
+       case 4:
+               reloc |= 0x3000;
+               break;
+       case 2:
+               reloc |= 0x2000;
+               break;
+       default:
+               fprintf ( stderr, "Unsupported relocation size %zd\n", size );
+               exit ( 1 );
+       }
+
+       /* Locate or create PE relocation table */
+       for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
+               if ( pe_rel->start_rva == start_rva )
+                       break;
+       }
+       if ( ! pe_rel ) {
+               pe_rel = xmalloc ( sizeof ( *pe_rel ) );
+               memset ( pe_rel, 0, sizeof ( *pe_rel ) );
+               pe_rel->next = *pe_reltab;
+               *pe_reltab = pe_rel;
+               pe_rel->start_rva = start_rva;
+       }
+
+       /* Expand relocation list if necessary */
+       if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
+               relocs = pe_rel->relocs;
+       } else {
+               pe_rel->total_relocs = ( pe_rel->total_relocs ?
+                                        ( pe_rel->total_relocs * 2 ) : 256 );
+               relocs = xmalloc ( pe_rel->total_relocs *
+                                  sizeof ( pe_rel->relocs[0] ) );
+               memset ( relocs, 0,
+                        pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) );
+               memcpy ( relocs, pe_rel->relocs,
+                        pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) );
+               free ( pe_rel->relocs );
+               pe_rel->relocs = relocs;
+       }
+
+       /* Store relocation */
+       pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
+}
+
+/**
+ * Calculate size of binary PE relocation table
+ *
+ * @v pe_reltab                PE relocation table
+ * @v buffer           Buffer to contain binary table, or NULL
+ * @ret size           Size of binary table
+ */
+static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
+                                void *buffer ) {
+       struct pe_relocs *pe_rel;
+       unsigned int num_relocs;
+       size_t size;
+       size_t total_size = 0;
+
+       for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
+               num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 );
+               size = ( sizeof ( uint32_t ) /* VirtualAddress */ +
+                        sizeof ( uint32_t ) /* SizeOfBlock */ +
+                        ( num_relocs * sizeof ( uint16_t ) ) );
+               if ( buffer ) {
+                       *( (uint32_t *) ( buffer + total_size + 0 ) )
+                               = pe_rel->start_rva;
+                       *( (uint32_t *) ( buffer + total_size + 4 ) ) = size;
+                       memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs,
+                                ( num_relocs * sizeof ( uint16_t ) ) );
+               }
+               total_size += size;
+       }
+
+       return total_size;
+}
+
+/**
+ * Read symbol table
+ *
+ * @v bfd              BFD file
+ */
+static void read_symtab ( struct bfd_file *bfd ) {
+       long symtab_size;
+
+       /* Get symbol table size */
+       symtab_size = bfd_get_symtab_upper_bound ( bfd->bfd );
+       if ( symtab_size < 0 ) {
+               bfd_perror ( "Could not get symbol table upper bound" );
+               exit ( 1 );
+       }
+
+       /* Allocate and read symbol table */
+       bfd->symtab = xmalloc ( symtab_size );
+       bfd->symcount = bfd_canonicalize_symtab ( bfd->bfd, bfd->symtab );
+       if ( bfd->symcount < 0 ) {
+               bfd_perror ( "Cannot read symbol table" );
+               exit ( 1 );
+       }
+}
+
+/**
+ * Read relocation table
+ *
+ * @v bfd              BFD file
+ * @v section          Section
+ * @v symtab           Symbol table
+ * @ret reltab         Relocation table
+ */
+static arelent ** read_reltab ( struct bfd_file *bfd, asection *section ) {
+       long reltab_size;
+       arelent **reltab;
+       long numrels;
+
+       /* Get relocation table size */
+       reltab_size = bfd_get_reloc_upper_bound ( bfd->bfd, section );
+       if ( reltab_size < 0 ) {
+               bfd_perror ( "Could not get relocation table upper bound" );
+               exit ( 1 );
+       }
+
+       /* Allocate and read relocation table */
+       reltab = xmalloc ( reltab_size );
+       numrels = bfd_canonicalize_reloc ( bfd->bfd, section, reltab,
+                                          bfd->symtab );
+       if ( numrels < 0 ) {
+               bfd_perror ( "Cannot read relocation table" );
+               exit ( 1 );
+       }
+
+       return reltab;
+}
+
+
+/**
+ * Open input BFD file
+ *
+ * @v filename         File name
+ * @ret ibfd           BFD file
+ */
+static struct bfd_file * open_input_bfd ( const char *filename ) {
+       struct bfd_file *ibfd;
+
+       /* Create BFD file */
+       ibfd = xmalloc ( sizeof ( *ibfd ) );
+       memset ( ibfd, 0, sizeof ( *ibfd ) );
+
+       /* Open the file */
+       ibfd->bfd = bfd_openr ( filename, NULL );
+       if ( ! ibfd->bfd ) {
+               fprintf ( stderr, "Cannot open %s: ", filename );
+               bfd_perror ( NULL );
+               exit ( 1 );
+       }
+
+       /* The call to bfd_check_format() must be present, otherwise
+        * we get a segfault from later BFD calls.
+        */
+       if ( bfd_check_format ( ibfd->bfd, bfd_object ) < 0 ) {
+               fprintf ( stderr, "%s is not an object file\n", filename );
+               exit ( 1 );
+       }
+
+       /* Read symbols and relocation entries */
+       read_symtab ( ibfd );
+
+       return ibfd;
+}
+
+/**
+ * Open output BFD file
+ *
+ * @v filename         File name
+ * @v ibfd             Input BFD file
+ * @ret obfd           BFD file
+ */
+static struct bfd_file * open_output_bfd ( const char *filename,
+                                          struct bfd_file *ibfd ) {
+       struct bfd_file *obfd;
+       asection *isection;
+       asection *osection;
+
+       /*
+        * Most of this code is based on what objcopy.c does.
+        *
+        */
+
+       /* Create BFD file */
+       obfd = xmalloc ( sizeof ( *obfd ) );
+       memset ( obfd, 0, sizeof ( *obfd ) );
+
+       /* Open the file */
+       obfd->bfd = bfd_openw ( filename, ibfd->bfd->xvec->name );
+       if ( ! obfd->bfd ) {
+               fprintf ( stderr, "Cannot open %s: ", filename );
+               bfd_perror ( NULL );
+               exit ( 1 );
+       }
+
+       /* Copy per-file data */
+       if ( ! bfd_set_arch_mach ( obfd->bfd, bfd_get_arch ( ibfd->bfd ),
+                                  bfd_get_mach ( ibfd->bfd ) ) ) {
+               bfd_perror ( "Cannot copy architecture" );
+               exit ( 1 );
+       }
+       if ( ! bfd_set_format ( obfd->bfd, bfd_get_format ( ibfd->bfd ) ) ) {
+               bfd_perror ( "Cannot copy format" );
+               exit ( 1 );
+       }
+       if ( ! bfd_copy_private_header_data ( ibfd->bfd, obfd->bfd ) ) {
+               bfd_perror ( "Cannot copy private header data" );
+               exit ( 1 );
+       }
+
+       /* Create sections */
+       for ( isection = ibfd->bfd->sections ; isection ;
+             isection = isection->next ) {
+               osection = bfd_make_section_anyway ( obfd->bfd,
+                                                    isection->name );
+               if ( ! osection ) {
+                       bfd_perror ( "Cannot create section" );
+                       exit ( 1 );
+               }
+               if ( ! bfd_set_section_flags ( obfd->bfd, osection,
+                                              isection->flags ) ) {
+                       bfd_perror ( "Cannot copy section flags" );
+                       exit ( 1 );
+               }
+               if ( ! bfd_set_section_size ( obfd->bfd, osection,
+                                bfd_section_size ( ibfd->bfd, isection ) ) ) {
+                       bfd_perror ( "Cannot copy section size" );
+                       exit ( 1 );
+               }
+               if ( ! bfd_set_section_vma ( obfd->bfd, osection,
+                                 bfd_section_vma ( ibfd->bfd, isection ) ) ) {
+                       bfd_perror ( "Cannot copy section VMA" );
+                       exit ( 1 );
+               }
+               osection->lma = bfd_section_lma ( ibfd->bfd, isection );
+               if ( ! bfd_set_section_alignment ( obfd->bfd, osection,
+                           bfd_section_alignment ( ibfd->bfd, isection ) ) ) {
+                       bfd_perror ( "Cannot copy section alignment" );
+                       exit ( 1 );
+               }
+               osection->entsize = isection->entsize;
+               isection->output_section = osection;
+               isection->output_offset = 0;
+               if ( ! bfd_copy_private_section_data ( ibfd->bfd, isection,
+                                                      obfd->bfd, osection ) ){
+                       bfd_perror ( "Cannot copy section private data" );
+                       exit ( 1 );
+               }
+       }
+
+       /* Copy symbol table */
+       bfd_set_symtab ( obfd->bfd, ibfd->symtab, ibfd->symcount );
+       obfd->symtab = ibfd->symtab;
+
+       return obfd;
+}
+
+/**
+ * Copy section from input BFD file to output BFD file
+ *
+ * @v obfd             Output BFD file
+ * @v ibfd             Input BFD file
+ * @v section          Section
+ */
+static void copy_bfd_section ( struct bfd_file *obfd, struct bfd_file *ibfd,
+                              asection *isection ) {
+       size_t size;
+       void *buf;
+       arelent **reltab;
+       arelent **rel;
+       char *errmsg;
+
+       /* Read in original section */
+       size = bfd_section_size ( ibfd->bfd, isection );
+       if ( ! size )
+               return;
+       buf = xmalloc ( size );
+       if ( ( ! bfd_get_section_contents ( ibfd->bfd, isection,
+                                           buf, 0, size ) ) ) {
+               fprintf ( stderr, "Cannot read section %s: ", isection->name );
+               bfd_perror ( NULL );
+               exit ( 1 );
+       }
+
+       /* Perform relocations.  We do this here, rather than letting
+        * ld do it for us when creating the input ELF file, so that
+        * we can change symbol values as a result of having created
+        * the .reloc section.
+        */
+       reltab = read_reltab ( ibfd, isection );
+       for ( rel = reltab ; *rel ; rel++ ) {
+               bfd_perform_relocation ( ibfd->bfd, *rel, buf, isection,
+                                        NULL, &errmsg );
+       }
+       free ( reltab );
+
+       /* Write out modified section */
+       if ( ( ! bfd_set_section_contents ( obfd->bfd,
+                                           isection->output_section,
+                                           buf, 0, size ) ) ) {
+               fprintf ( stderr, "Cannot write section %s: ",
+                         isection->output_section->name );
+               bfd_perror ( NULL );
+               exit ( 1 );
+       }
+
+       free ( buf );
+}
+
+/**
+ * Process relocation record
+ *
+ * @v section          Section
+ * @v rel              Relocation entry
+ * @v pe_reltab                PE relocation table to fill in
+ */
+static void process_reloc ( asection *section, arelent *rel,
+                           struct pe_relocs **pe_reltab ) {
+       reloc_howto_type *howto = rel->howto;
+       asymbol *sym = *(rel->sym_ptr_ptr);
+       unsigned long offset = ( section->lma + rel->address );
+
+       if ( bfd_is_abs_section ( sym->section ) ) {
+               /* Skip absolute symbols; the symbol value won't
+                * change when the object is loaded.
+                */
+       } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) {
+               /* Generate a 4-byte PE relocation */
+               generate_pe_reloc ( pe_reltab, offset, 4 );
+       } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) {
+               /* Generate a 2-byte PE relocation */
+               generate_pe_reloc ( pe_reltab, offset, 2 );
+       } else if ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) {
+               /* Skip PC-relative relocations; all relative offsets
+                * remain unaltered when the object is loaded.
+                */
+       } else {
+               fprintf ( stderr, "Unrecognised relocation type %s\n",
+                         howto->name );
+               exit ( 1 );
+       }
+}
+
+/**
+ * Create .reloc section
+ *
+ * obfd                        Output BFD file
+ * section             .reloc section in output file
+ * pe_reltab           PE relocation table
+ */
+static void create_reloc_section ( struct bfd_file *obfd, asection *section,
+                                  struct pe_relocs *pe_reltab ) {
+       size_t raw_size;
+       size_t size;
+       size_t old_size;
+       void *buf;
+       asymbol **sym;
+
+       /* Build binary PE relocation table */
+       raw_size = output_pe_reltab ( pe_reltab, NULL );
+       size = ( ( raw_size + 31 ) & ~31 );
+       buf = xmalloc ( size );
+       memset ( buf, 0, size );
+       output_pe_reltab ( pe_reltab, buf );
+
+       /* Write .reloc section */
+       old_size = bfd_section_size ( obfd->bfd, section );
+       if ( ! bfd_set_section_size ( obfd->bfd, section, size ) ) {
+               bfd_perror ( "Cannot resize .reloc section" );
+               exit ( 1 );
+       }
+       if ( ! bfd_set_section_contents ( obfd->bfd, section,
+                                         buf, 0, size ) ) {
+               bfd_perror ( "Cannot set .reloc section contents" );
+               exit ( 1 );
+       }
+
+       /* Update symbols pertaining to the relocation directory */
+       for ( sym = obfd->symtab ; *sym ; sym++ ) {
+               if ( strcmp ( (*sym)->name, "_reloc_memsz" ) == 0 ) {
+                       (*sym)->value = size;
+               } else if ( strcmp ( (*sym)->name, "_reloc_filesz" ) == 0 ){
+                       (*sym)->value = raw_size;
+               } else if ( strcmp ( (*sym)->name, "_filesz" ) == 0 ) {
+                       (*sym)->value += ( size - old_size );
+               }
+       }
+}
+
+int main ( int argc, const char *argv[] ) {
+       const char *iname;
+       const char *oname;
+       struct bfd_file *ibfd;
+       struct bfd_file *obfd;
+       asection *section;
+       arelent **reltab;
+       arelent **rel;
+       struct pe_relocs *pe_reltab = NULL;
+       asection *reloc_section;
+
+       /* Initialise libbfd */
+       bfd_init();
+
+       /* Identify intput and output files */
+       if ( argc != 3 ) {
+               fprintf ( stderr, "Syntax: %s infile outfile\n", argv[0] );
+               exit ( 1 );
+       }
+       iname = argv[1];
+       oname = argv[2];
+
+       /* Open BFD files */
+       ibfd = open_input_bfd ( iname );
+       obfd = open_output_bfd ( oname, ibfd );
+
+       /* Process relocations in all sections */
+       for ( section = ibfd->bfd->sections ; section ;
+             section = section->next ) {
+               reltab = read_reltab ( ibfd, section );
+               for ( rel = reltab ; *rel ; rel++ ) {
+                       process_reloc ( section, *rel, &pe_reltab );
+               }
+               free ( reltab );
+       }
+
+       /* Create modified .reloc section */
+       reloc_section = bfd_get_section_by_name ( obfd->bfd, ".reloc" );
+       if ( ! reloc_section ) {
+               fprintf ( stderr, "Cannot find .reloc section\n" );
+               exit ( 1 );
+       }
+       create_reloc_section ( obfd, reloc_section, pe_reltab );
+
+       /* Copy other section contents */
+       for ( section = ibfd->bfd->sections ; section ;
+             section = section->next ) {
+               if ( section->output_section != reloc_section )
+                       copy_bfd_section ( obfd, ibfd, section );
+       }
+
+       /* Write out files and clean up */
+       bfd_close ( obfd->bfd );
+       bfd_close ( ibfd->bfd );
+
+       return 0;
+}