[efi] Allow for .efidrv images as well as .efi images
authorMichael Brown <mcb30@etherboot.org>
Wed, 7 Jan 2009 23:43:26 +0000 (23:43 +0000)
committerMichael Brown <mcb30@etherboot.org>
Wed, 7 Jan 2009 23:43:26 +0000 (23:43 +0000)
Merge in the changes that allow for building EFI driver images (that
can be loaded using the EFI shell's "load" command) as well as EFI
applications.

src/arch/i386/Makefile.efi
src/arch/x86/Makefile.efi [new file with mode: 0644]
src/arch/x86/prefix/efidrvprefix.c [new file with mode: 0644]
src/arch/x86/scripts/efi.lds
src/arch/x86_64/Makefile.efi
src/include/gpxe/efi/efi.h
src/util/elf2efi.c

index c75338c..8d651b0 100644 (file)
@@ -1,19 +1,10 @@
 # -*- makefile -*- : Force emacs to use Makefile mode
 
-# The EFI linker script
+# Specify EFI image builder
 #
-LDSCRIPT       = arch/x86/scripts/efi.lds
+ELF2EFI                = $(ELF2EFI32)
 
-# Retain relocation information for elf2efi
+# Include generic EFI Makefile
 #
-LDFLAGS                += -q -S
-
-# Media types.
-#
-NON_AUTO_MEDIA += efi
-
-# Rule for building EFI files
-#
-$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI32)
-       $(QM)$(ECHO) "  [FINISH] $@"
-       $(Q)$(ELF2EFI32) $< $@
+MAKEDEPS       += arch/x86/Makefile.efi
+include arch/x86/Makefile.efi
diff --git a/src/arch/x86/Makefile.efi b/src/arch/x86/Makefile.efi
new file mode 100644 (file)
index 0000000..92cd020
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# The EFI linker script
+#
+LDSCRIPT       = arch/x86/scripts/efi.lds
+
+# Retain relocation information for elf2efi
+#
+LDFLAGS                += -q -S
+
+# Media types.
+#
+NON_AUTO_MEDIA += efi
+NON_AUTO_MEDIA += efidrv
+
+# Rules for building EFI files
+#
+$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI)
+       $(QM)$(ECHO) "  [FINISH] $@"
+       $(Q)$(ELF2EFI) --subsystem=10 $< $@
+
+$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI)
+       $(QM)$(ECHO) "  [FINISH] $@"
+       $(Q)$(ELF2EFI) --subsystem=11 $< $@
diff --git a/src/arch/x86/prefix/efidrvprefix.c b/src/arch/x86/prefix/efidrvprefix.c
new file mode 100644 (file)
index 0000000..5f63158
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 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/init.h>
+#include <gpxe/efi/efi.h>
+
+/**
+ * EFI entry point
+ *
+ * @v image_handle     Image handle
+ * @v systab           System table
+ * @ret efirc          EFI return status code
+ */
+EFI_STATUS EFIAPI _start ( EFI_HANDLE image_handle,
+                          EFI_SYSTEM_TABLE *systab ) {
+       EFI_STATUS efirc;
+
+       /* Initialise EFI environment */
+       if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
+               return efirc;
+
+       /* Initialise gPXE environment */
+       initialise();
+       startup();
+
+       /* Install SNP driver and return */
+       return RC_TO_EFIRC ( efi_snp_install () );
+}
index bfe8173..aac3105 100644 (file)
@@ -5,7 +5,6 @@
  *
  */
 
-EXTERN ( _start )
 ENTRY ( _start )
 
 SECTIONS {
index 928f5e9..26b7127 100644 (file)
@@ -4,20 +4,11 @@
 #
 CFLAGS         += -mno-red-zone
 
-# The EFI linker script
+# Specify EFI image builder
 #
-LDSCRIPT       = arch/x86/scripts/efi.lds
+ELF2EFI                = $(ELF2EFI64)
 
-# Retain relocation information for elf2efi
+# Include generic EFI Makefile
 #
-LDFLAGS                += -q -S
-
-# Media types.
-#
-NON_AUTO_MEDIA += efi
-
-# Rule for building EFI files
-#
-$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI64)
-       $(QM)$(ECHO) "  [FINISH] $@"
-       $(Q)$(ELF2EFI64) $< $@
+MAKEDEPS       += arch/x86/Makefile.efi
+include arch/x86/Makefile.efi
index 2f99514..c7f63b6 100644 (file)
@@ -126,5 +126,6 @@ extern EFI_SYSTEM_TABLE *efi_systab;
 extern const char * efi_strerror ( EFI_STATUS efirc );
 extern EFI_STATUS efi_init ( EFI_HANDLE image_handle,
                             EFI_SYSTEM_TABLE *systab );
+extern int efi_snp_install ( void );
 
 #endif /* _EFI_H */
index 0c65a44..ec75be0 100644 (file)
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
+#include <getopt.h>
 #include <bfd.h>
 
 /* Include the EFI PE image header file */
@@ -69,6 +70,8 @@ typedef uint64_t UINT64;
 #define  BIT31    0x80000000
 #include "../include/gpxe/efi/IndustryStandard/PeImage.h"
 
+#define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
+
 #define EFI_FILE_ALIGN 0x20
 
 struct pe_section {
@@ -127,13 +130,17 @@ static struct pe_header efi_pe_header = {
                        .FileAlignment = EFI_FILE_ALIGN,
                        .SizeOfImage = sizeof ( efi_pe_header ),
                        .SizeOfHeaders = sizeof ( efi_pe_header ),
-                       .Subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
                        .NumberOfRvaAndSizes =
                                EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
                },
        },
 };
 
+/** Command-line options */
+struct options {
+       unsigned int subsystem;
+};
+
 /**
  * Allocate memory
  *
@@ -145,7 +152,7 @@ static void * xmalloc ( size_t len ) {
 
        ptr = malloc ( len );
        if ( ! ptr ) {
-               fprintf ( stderr, "Could not allocate %zd bytes\n", len );
+               eprintf ( "Could not allocate %zd bytes\n", len );
                exit ( 1 );
        }
 
@@ -190,7 +197,7 @@ static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
                reloc |= 0x2000;
                break;
        default:
-               fprintf ( stderr, "Unsupported relocation size %zd\n", size );
+               eprintf ( "Unsupported relocation size %zd\n", size );
                exit ( 1 );
        }
 
@@ -271,7 +278,7 @@ static bfd * open_input_bfd ( const char *filename ) {
        /* Open the file */
        bfd = bfd_openr ( filename, NULL );
        if ( ! bfd ) {
-               fprintf ( stderr, "Cannot open %s: ", filename );
+               eprintf ( "Cannot open %s: ", filename );
                bfd_perror ( NULL );
                exit ( 1 );
        }
@@ -280,7 +287,7 @@ static bfd * open_input_bfd ( const char *filename ) {
         * we get a segfault from later BFD calls.
         */
        if ( bfd_check_format ( bfd, bfd_object ) < 0 ) {
-               fprintf ( stderr, "%s is not an object file\n", filename );
+               eprintf ( "%s is not an object file\n", filename );
                exit ( 1 );
        }
 
@@ -442,8 +449,7 @@ static struct pe_section * process_section ( bfd *bfd,
        if ( flags & SEC_LOAD ) {
                if ( ! bfd_get_section_contents ( bfd, section, new->contents,
                                                  0, section_memsz ) ) {
-                       fprintf ( stderr, "Cannot read section %s: ",
-                                 section->name );
+                       eprintf ( "Cannot read section %s: ", section->name );
                        bfd_perror ( NULL );
                        exit ( 1 );
                }
@@ -518,8 +524,7 @@ static void process_reloc ( bfd *bfd, asection *section, arelent *rel,
                 * remain unaltered when the object is loaded.
                 */
        } else {
-               fprintf ( stderr, "Unrecognised relocation type %s\n",
-                         howto->name );
+               eprintf ( "Unrecognised relocation type %s\n", howto->name );
                exit ( 1 );
        }
 }
@@ -670,7 +675,7 @@ static void write_pe_file ( struct pe_header *pe_header,
        for ( section = pe_sections ; section ; section = section->next ) {
                if ( fseek ( pe, section->hdr.PointerToRawData,
                             SEEK_SET ) != 0 ) {
-                       fprintf ( stderr, "Could not seek to %lx: %s\n",
+                       eprintf ( "Could not seek to %lx: %s\n",
                                  section->hdr.PointerToRawData,
                                  strerror ( errno ) );
                        exit ( 1 );
@@ -678,7 +683,7 @@ static void write_pe_file ( struct pe_header *pe_header,
                if ( section->hdr.SizeOfRawData &&
                     ( fwrite ( section->contents, section->hdr.SizeOfRawData,
                                1, pe ) != 1 ) ) {
-                       fprintf ( stderr, "Could not write section %.8s: %s\n",
+                       eprintf ( "Could not write section %.8s: %s\n",
                                  section->hdr.Name, strerror ( errno ) );
                        exit ( 1 );
                }
@@ -691,7 +696,8 @@ static void write_pe_file ( struct pe_header *pe_header,
  * @v elf_name         ELF file name
  * @v pe_name          PE file name
  */
-static void elf2pe ( const char *elf_name, const char *pe_name ) {
+static void elf2pe ( const char *elf_name, const char *pe_name,
+                    struct options *opts ) {
        bfd *bfd;
        asymbol **symtab;
        asection *section;
@@ -711,6 +717,7 @@ static void elf2pe ( const char *elf_name, const char *pe_name ) {
        memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) );
        pe_header.nt.OptionalHeader.AddressOfEntryPoint =
                bfd_get_start_address ( bfd );
+       pe_header.nt.OptionalHeader.Subsystem = opts->subsystem;
 
        /* For each input section, build an output section and create
         * the appropriate relocation records
@@ -742,7 +749,7 @@ static void elf2pe ( const char *elf_name, const char *pe_name ) {
        /* Write out PE file */
        pe = fopen ( pe_name, "w" );
        if ( ! pe ) {
-               fprintf ( stderr, "Could not open %s for writing: %s\n",
+               eprintf ( "Could not open %s for writing: %s\n",
                          pe_name, strerror ( errno ) );
                exit ( 1 );
        }
@@ -753,12 +760,84 @@ static void elf2pe ( const char *elf_name, const char *pe_name ) {
        bfd_close ( bfd );
 }
 
+/**
+ * Print help
+ *
+ * @v program_name     Program name
+ */
+static void print_help ( const char *program_name ) {
+       eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n",
+                 program_name );
+}
+
+/**
+ * Parse command-line options
+ *
+ * @v argc             Argument count
+ * @v argv             Argument list
+ * @v opts             Options structure to populate
+ */
+static int parse_options ( const int argc, char **argv,
+                          struct options *opts ) {
+       char *end;
+       int c;
+
+       while (1) {
+               int option_index = 0;
+               static struct option long_options[] = {
+                       { "subsystem", required_argument, NULL, 's' },
+                       { "help", 0, NULL, 'h' },
+                       { 0, 0, 0, 0 }
+               };
+
+               if ( ( c = getopt_long ( argc, argv, "s:h",
+                                        long_options,
+                                        &option_index ) ) == -1 ) {
+                       break;
+               }
+
+               switch ( c ) {
+               case 's':
+                       opts->subsystem = strtoul ( optarg, &end, 0 );
+                       if ( *end ) {
+                               eprintf ( "Invalid subsytem \"%s\"\n",
+                                         optarg );
+                               exit ( 2 );
+                       }
+                       break;
+               case 'h':
+                       print_help ( argv[0] );
+                       exit ( 0 );
+               case '?':
+               default:
+                       exit ( 2 );
+               }
+       }
+       return optind;
+}
+
 int main ( int argc, char **argv ) {
+       struct options opts = {
+               .subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
+       };
+       unsigned int infile_index;
+       const char *infile;
+       const char *outfile;
 
        /* Initialise libbfd */
        bfd_init();
 
-       elf2pe ( argv[1], argv[2] );
+       /* Parse command-line arguments */
+       infile_index = parse_options ( argc, argv, &opts );
+       if ( argc != ( infile_index + 2 ) ) {
+               print_help ( argv[0] );
+               exit ( 2 );
+       }
+       infile = argv[infile_index];
+       outfile = argv[infile_index + 1];
+
+       /* Convert file */
+       elf2pe ( infile, outfile, &opts );
 
        return 0;
 }