aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2011-12-02 18:02:09 +0000
committerMatt Fleming <matt.fleming@intel.com>2011-12-16 16:31:19 +0000
commitd1195567fe9e6b580b04c73d78bb923383ac2dc9 (patch)
tree5681a2f6d895cc280999c491f46777192d713bfb
parent04303c2561333b937c30e37732cefe1df0abb280 (diff)
downloadsyslinux-d1195567fe9e6b580b04c73d78bb923383ac2dc9.tar.gz
syslinux-d1195567fe9e6b580b04c73d78bb923383ac2dc9.tar.xz
syslinux-d1195567fe9e6b580b04c73d78bb923383ac2dc9.zip
efi: Add UEFI firmware backend
This commit adds 32-bit support for a Unified Extensible Firmware Interface backend. We use a wrapper program to create an EFI exe with only the minimum number of sections becaues the EFI loader doesn't like certain section sections, such as .gnu.hash, which we require for resolving symbols when ELF modules are loaded. We're currently including a lot more symbols than are necessary in efi/main.c so that the code compiles. Some of them are BIOS specific but I haven't got around to fixing up the core to not reference them directly yet. Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--Makefile2
-rw-r--r--efi/Makefile66
-rw-r--r--efi/console.c6
-rw-r--r--efi/efi.h9
-rw-r--r--efi/main.c298
-rw-r--r--efi/syslinux.ld177
-rw-r--r--efi/wrapper.c185
-rw-r--r--efi/wrapper.h112
-rw-r--r--mk/efi.mk37
9 files changed, 891 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 0f500a33..0e799101 100644
--- a/Makefile
+++ b/Makefile
@@ -54,7 +54,7 @@ BOBJECTS = $(BTARGET) \
# files that depend only on the B phase, but may have to be regenerated
# for "make installer".
BSUBDIRS = codepage com32 lzo core memdisk modules mbr memdump gpxe sample \
- diag libinstaller dos win32 win64 dosutil
+ diag libinstaller dos win32 win64 dosutil efi
ITARGET =
IOBJECTS = $(ITARGET) \
utils/gethostip utils/isohybrid utils/mkdiskimage \
diff --git a/efi/Makefile b/efi/Makefile
new file mode 100644
index 00000000..294d8556
--- /dev/null
+++ b/efi/Makefile
@@ -0,0 +1,66 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2011 Intel Corporation; author: Matt Fleming
+##
+## 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, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ..
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/lib.mk
+include $(MAKEDIR)/efi.mk
+
+CORE_CSRC := $(wildcard $(core)/*.c $(core)/*/*.c $(core)/*/*/*.c)
+CORE_COBJ := $(patsubst %.c,%.o,$(CORE_CSRC))
+
+# Don't include console objects
+CORE_OBJS = $(filter-out $(core)/hello.o $(core)/rawcon.o \
+ $(core)/plaincon.o $(core)/strcasecmp.o,$(CORE_COBJ))
+LIB_OBJS = $(addprefix $(com32)/lib/,$(MINLIBOBJS))
+
+CSRC = $(wildcard *.c)
+OBJ = $(patsubst %.c,%.o,$(CSRC))
+OBJS = $(filter-out wrapper.o,$(OBJ))
+
+OBJS += $(core)/codepage.o
+
+# The targets to build in this directory
+BTARGET = syslinux.efi
+
+syslinux.so: $(OBJS) $(CORE_OBJS) $(LIB_OBJS)
+ $(LD) $(LDFLAGS) -o $@ $^ -lgnuefi -lefi
+
+# We need to rename the .hash section because the EFI firmware
+# linker really doesn't like it.
+# $(OBJCOPY) --rename-section .gnu.hash=.sdata,load,data,alloc $^ $@
+#syslinux.so: syslinux1.so
+# cp $^ $@
+
+wrapper: wrapper.c
+ $(CC) $^ -o $@
+
+#
+# Build the wrapper app and wrap our .so to produce a .efi
+syslinux.efi: syslinux.so wrapper
+ ./wrapper syslinux.so $@
+
+all: $(BTARGET)
+
+$(core)/codepage.o: ../codepage/cp865.cp
+ cp ../codepage/cp865.cp codepage.cp
+ $(CC) $(SFLAGS) -c -o $@ $(core)/codepage.S
+
+tidy dist:
+ rm -f *.so *.o
+ find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \
+ xargs -0r rm -f
+
+clean: tidy
+
+spotless: clean
+ rm -f $(BTARGET)
diff --git a/efi/console.c b/efi/console.c
new file mode 100644
index 00000000..baca9733
--- /dev/null
+++ b/efi/console.c
@@ -0,0 +1,6 @@
+#include "efi.h"
+
+void writechr(char data)
+{
+ Print(L"Wanted to print something\n");
+}
diff --git a/efi/efi.h b/efi/efi.h
new file mode 100644
index 00000000..ac5ca3fd
--- /dev/null
+++ b/efi/efi.h
@@ -0,0 +1,9 @@
+#ifndef _SYSLINUX_EFI_H
+#define _SYSLINUX_EFI_H
+
+#include <core.h>
+#include <syslinux/version.h>
+#include <efi.h>
+#include <efilib.h>
+
+#endif /* _SYSLINUX_EFI_H */
diff --git a/efi/main.c b/efi/main.c
new file mode 100644
index 00000000..469a0a68
--- /dev/null
+++ b/efi/main.c
@@ -0,0 +1,298 @@
+#include <core.h>
+#include <fs.h>
+#include <com32.h>
+#include <syslinux/memscan.h>
+#include <syslinux/firmware.h>
+#include <sys/ansi.h>
+
+#include "efi.h"
+
+char KernelName[FILENAME_MAX];
+uint16_t PXERetry;
+char copyright_str[] = "Copyright (C) 2011\n";
+uint8_t SerialNotice = 1;
+char syslinux_banner[] = "Syslinux 5.x (EFI)\n";
+char CurrentDirName[FILENAME_MAX];
+struct com32_sys_args __com32;
+
+uint32_t _IdleTimer = 0;
+uint16_t NoHalt = 0;
+char __lowmem_heap[32];
+uint32_t BIOS_timer_next;
+uint32_t timer_irq;
+uint8_t KbdMap[256];
+uint16_t VGAFontSize = 16;
+char aux_seg[256];
+uint8_t UserFont = 0;
+
+#undef kaboom
+void kaboom(void)
+{
+}
+
+void comboot_cleanup_api(void)
+{
+}
+
+void printf_init(void)
+{
+}
+
+void local_boot16(void)
+{
+}
+
+void bios_timer_cleanup(void)
+{
+}
+
+char trackbuf[4096];
+
+void __cdecl core_farcall(uint32_t c, const com32sys_t *a, com32sys_t *b)
+{
+}
+
+void *__syslinux_adv_ptr; /* definitely needs to die: is in ldlinux now */
+size_t __syslinux_adv_size; /* definitely needs to die: is in ldlinux now */
+char core_xfer_buf[65536];
+struct iso_boot_info {
+ uint32_t pvd; /* LBA of primary volume descriptor */
+ uint32_t file; /* LBA of boot file */
+ uint32_t length; /* Length of boot file */
+ uint32_t csum; /* Checksum of boot file */
+ uint32_t reserved[10]; /* Currently unused */
+} iso_boot_info;
+
+struct ip_info {
+ uint32_t ipv4;
+ uint32_t myip;
+ uint32_t serverip;
+ uint32_t gateway;
+ uint32_t netmask;
+} IPInfo;
+
+uint8_t DHCPMagic;
+uint32_t RebootTime;
+
+void pxenv(void)
+{
+}
+
+uint16_t IPAppends = 0;
+char numIPAppends[2];
+uint16_t BIOS_fbm = 1;
+far_ptr_t InitStack;
+uint16_t APIVer;
+far_ptr_t PXEEntry;
+
+void gpxe_unload(void)
+{
+}
+
+void do_idle(void)
+{
+}
+
+void pxe_int1a(void)
+{
+}
+
+uint8_t KeepPXE;
+
+
+volatile uint32_t __ms_timer = 0xdeadbeef;
+volatile uint32_t __jiffies = 0;
+
+static UINTN cursor_x, cursor_y;
+static void efi_erase(const struct term_state *st,
+ int x0, int y0, int x1, int y1)
+{
+ cursor_x = cursor_y = 0;
+}
+
+static void efi_write_char(uint8_t ch, uint8_t attribute)
+{
+ SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
+ uint16_t c[2];
+
+ c[0] = ch;
+ c[1] = '\0';
+ uefi_call_wrapper(out->OutputString, 2, out, c);
+}
+
+static void efi_showcursor(uint16_t cursor)
+{
+ SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
+ uefi_call_wrapper(out->SetCursorPosition, 3, out, cursor_x, cursor_y);
+}
+
+static void efi_set_cursor(int x, int y, bool visible)
+{
+ SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
+
+ if (visible) {
+ uefi_call_wrapper(out->SetCursorPosition, 3, out, x, y);
+ cursor_x = x;
+ cursor_y = y;
+ } else
+ uefi_call_wrapper(out->EnableCursor, 2, out, false);
+}
+
+static void efi_scroll_up(uint8_t cols, uint8_t rows, uint8_t attribute)
+{
+}
+
+
+static void efi_get_mode(int *rows, int *cols)
+{
+ SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut;
+ UINTN c, r;
+
+ /* XXX: Assume we're at 80x25 for now (mode 0) */
+ uefi_call_wrapper(out->QueryMode, 4, out, 0, &c, &r);
+ *rows = r;
+ *cols = c;
+}
+
+static void efi_set_mode(uint16_t mode)
+{
+}
+
+static void efi_get_cursor(int *x, int *y)
+{
+ *x = cursor_x;
+ *y = cursor_y;
+}
+
+struct output_ops efi_ops = {
+ .erase = efi_erase,
+ .write_char = efi_write_char,
+ .showcursor = efi_showcursor,
+ .set_cursor = efi_set_cursor,
+ .scroll_up = efi_scroll_up,
+ .get_mode = efi_get_mode,
+ .set_mode = efi_set_mode,
+ .get_cursor = efi_get_cursor,
+};
+
+char SubvolName[2];
+static inline EFI_MEMORY_DESCRIPTOR *
+get_memory_map(UINTN *nr_entries, UINTN *key, UINTN *desc_sz,
+ uint32_t *desc_ver)
+{
+ return LibMemoryMap(nr_entries, key, desc_sz, desc_ver);
+}
+
+
+int efi_scan_memory(scan_memory_callback_t callback, void *data)
+{
+ UINTN nr_entries, key, desc_sz;
+ UINTN buf;
+ UINT32 desc_ver;
+ int rv = 0;
+ int i;
+
+ buf = (UINTN)get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver);
+ if (!buf)
+ return -1;
+
+ for (i = 0; i < nr_entries; buf += desc_sz, i++) {
+ EFI_MEMORY_DESCRIPTOR *m;
+ UINT64 region_sz;
+ int valid;
+
+ m = (EFI_MEMORY_DESCRIPTOR *)buf;
+ region_sz = m->NumberOfPages * EFI_PAGE_SIZE;
+
+ switch (m->Type) {
+ case EfiConventionalMemory:
+ valid = 1;
+ break;
+ default:
+ valid = 0;
+ break;
+ }
+
+ rv = callback(data, m->PhysicalStart, region_sz, valid);
+ if (rv)
+ break;
+ }
+
+ FreePool((void *)buf);
+ return rv;
+}
+
+extern uint16_t *bios_free_mem;
+void efi_init(void)
+{
+ /* XXX timer */
+ *bios_free_mem = 0;
+ mem_init();
+}
+
+char efi_getchar(void)
+{
+ SIMPLE_INPUT_INTERFACE *in = ST->ConIn;
+ EFI_INPUT_KEY key;
+ EFI_STATUS status;
+ char c;
+
+ do {
+ status = uefi_call_wrapper(in->ReadKeyStroke, 2, in, &key);
+ } while (status == EFI_NOT_READY);
+
+ c = (char)key.UnicodeChar;
+}
+
+struct input_ops efi_iops = {
+ .getchar = efi_getchar,
+};
+
+extern struct disk *efi_disk_init(com32sys_t *);
+struct firmware efi_fw = {
+ .init = efi_init,
+ .scan_memory = efi_scan_memory,
+ .disk_init = efi_disk_init,
+ .o_ops = &efi_ops,
+ .i_ops = &efi_iops,
+};
+
+static inline void syslinux_register_efi(void)
+{
+ firmware = &efi_fw;
+}
+
+extern void init(void);
+extern const struct fs_ops vfat_fs_ops;
+
+char free_high_memory[4096];
+
+extern char __bss_start[];
+extern char __bss_end[];
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
+{
+ EFI_LOADED_IMAGE *info;
+ EFI_STATUS status = EFI_SUCCESS;
+ struct fs_ops *ops[] = { &vfat_fs_ops, NULL };
+ unsigned long len = (unsigned long)__bss_end - (unsigned long)__bss_start;
+
+ memset(__bss_start, 0, len);
+ InitializeLib(image, table);
+
+ syslinux_register_efi();
+ init();
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3, image,
+ &LoadedImageProtocol, (void **)&info);
+ if (status != EFI_SUCCESS) {
+ printf("Failed to lookup LoadedImageProtocol\n");
+ goto out;
+ }
+
+ /* XXX figure out what file system we're on */
+ fs_init(ops, info->DeviceHandle);
+ load_env32();
+
+out:
+ return status;
+}
diff --git a/efi/syslinux.ld b/efi/syslinux.ld
new file mode 100644
index 00000000..5f8aa051
--- /dev/null
+++ b/efi/syslinux.ld
@@ -0,0 +1,177 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 2008-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * 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, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Linker script for the SYSLINUX core
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = 0;
+ ImageBase = .;
+ . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
+ .text : {
+ FILL(0x90909090)
+ __text_start = .;
+ *(.text)
+ *(.text.*)
+ __text_end = .;
+ }
+
+ . = ALIGN(16);
+
+ .rodata : {
+ __rodata_start = .;
+ *(.rodata)
+ *(.rodata.*)
+ __rodata_end = .;
+ }
+
+ . = ALIGN(4);
+
+ .ctors : {
+ __ctors_start = .;
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ __ctors_end = .;
+ }
+
+ .dtors : {
+ __dtors_start = .;
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ __dtors_end = .;
+ }
+
+ . = ALIGN(4096);
+ .rel : {
+ *(.rel.got)
+ *(.rel.data)
+ *(.rel.data.*)
+ *(.rel.ctors)
+ }
+
+ . = ALIGN(4);
+
+ .gnu.hash : {
+ __gnu_hash_start = .;
+ *(.gnu.hash)
+ __gnu_hash_end = .;
+ }
+
+
+ .dynsym : {
+ __dynsym_start = .;
+ *(.dynsym)
+ __dynsym_end = .;
+ }
+ __dynsym_len = __dynsym_end - __dynsym_start;
+
+ . = ALIGN(4);
+
+ .dynstr : {
+ __dynstr_start = .;
+ *(.dynstr)
+ __dynstr_end = .;
+ }
+ __dynstr_len = __dynstr_end - __dynstr_start;
+
+ . = ALIGN(4);
+
+ .dynlink : {
+ __dynlink_start = .;
+ *(.dynlink)
+ __dynlink_end = .;
+ }
+
+ . = ALIGN(4);
+
+ .got : {
+ __got_start = .;
+ KEEP (*(.got.plt))
+ KEEP (*(.got))
+ __got_end = .;
+ }
+
+ . = ALIGN(4);
+
+ .dynamic : {
+ __dynamic_start = .;
+ *(.dynamic)
+ __dynamic_end = .;
+ }
+
+ . = ALIGN(16);
+
+ .data : {
+ __data_start = .;
+ *(.data)
+ *(.data.*)
+ *(.lowmem)
+ __data_end = .;
+ }
+
+ .reloc : {
+ *(.reloc)
+ }
+
+ .comment : {
+ *(.commet)
+ }
+
+ .symtab : {
+ *(.symtab)
+ }
+
+ .strtab : {
+ *(.strtab)
+ }
+
+ .bss : {
+ /* the EFI loader doesn't seem to like a .bss section,
+ so we stick it all into .data: */
+ __bss_start = .;
+ *(.bss)
+ *(.bss.*)
+ *(.bss16)
+ *(.hugebss)
+ *(COMMON)
+ __bss_end = .;
+ *(.sbss)
+ *(.scommon)
+ }
+ __bss_len = ABSOLUTE(__bss_end) - ABSOLUTE(__bss_start);
+ __bss_dwords = (__bss_len + 3) >> 2;
+
+ . = ALIGN(128);
+
+ /* Very large objects which don't need to be zeroed */
+
+ .hugebss : {
+ __hugebss_start = .;
+ *(.hugebss)
+ *(.hugebss.*)
+ __hugebss_end = .;
+ }
+
+ _end = .;
+
+ /* Stuff we don't need... */
+ /DISCARD/ : {
+ *(.eh_frame)
+ }
+}
diff --git a/efi/wrapper.c b/efi/wrapper.c
new file mode 100644
index 00000000..203f79e9
--- /dev/null
+++ b/efi/wrapper.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2011 Intel Corporation; author Matt Fleming
+ *
+ * Wrap the ELF shared library in a PE32 suit.
+ *
+ * Syslinux plays some games with the ELF sections that are not easily
+ * converted to a PE32 executable. For instance, Syslinux requires
+ * that a symbol hash table be present (GNU hash or SysV) so that
+ * symbols in ELF modules can be resolved at runtime but the EFI
+ * firmware loader doesn't like that and refuses to load the file.
+ *
+ * We pretend that we have an EFI executable with a single .text
+ * section so that the EFI loader will load it and jump to the entry
+ * point. Once the Syslinux ELF shared object has control we can do
+ * whatever we want.
+ */
+#include <linux/elf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "wrapper.h"
+
+/*
+ * 'so_size' is the file size of the ELF shared object.
+ */
+static void write_header(FILE *f, __uint32_t entry, __uint32_t so_size)
+{
+ struct optional_hdr o_hdr;
+ struct section t_sec, r_sec;
+ struct extra_hdr e_hdr;
+ struct coff_hdr c_hdr;
+ struct header hdr;
+ struct coff_reloc c_rel;
+ __uint32_t total_sz = so_size;
+ __uint32_t dummy = 0;
+ __uint32_t hdr_sz;
+ __uint32_t reloc_start, reloc_end;
+
+ hdr_sz = sizeof(o_hdr) + sizeof(t_sec) + sizeof(e_hdr) +
+ sizeof(r_sec) + sizeof(c_hdr) + sizeof(hdr) + sizeof(c_rel)
+ + sizeof(dummy);
+ total_sz += hdr_sz;
+ entry += hdr_sz;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.msdos_signature = MSDOS_SIGNATURE;
+ hdr.pe_hdr = OFFSETOF(struct header, pe_signature);
+ hdr.pe_signature = PE_SIGNATURE;
+ fwrite(&hdr, sizeof(hdr), 1, f);
+
+ memset(&c_hdr, 0, sizeof(c_hdr));
+ c_hdr.arch = IMAGE_FILE_MACHINE_I386;
+ c_hdr.nr_sections = 2;
+ c_hdr.nr_syms = 1;
+ c_hdr.optional_hdr_sz = sizeof(o_hdr) + sizeof(e_hdr);
+ c_hdr.characteristics = IMAGE_FILE_32BIT_MACHINE |
+ IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE |
+ IMAGE_FILE_LINE_NUMBERS_STRIPPED;
+ fwrite(&c_hdr, sizeof(c_hdr), 1, f);
+
+ memset(&o_hdr, 0, sizeof(o_hdr));
+ o_hdr.format = PE32_FORMAT;
+ o_hdr.major_linker_version = 0x02;
+ o_hdr.minor_linker_version = 0x14;
+ o_hdr.code_sz = total_sz;
+ o_hdr.entry_point = entry;
+ fwrite(&o_hdr, sizeof(o_hdr), 1, f);
+
+ memset(&e_hdr, 0, sizeof(e_hdr));
+ e_hdr.section_align = 4096;
+ e_hdr.file_align = 512;
+ e_hdr.image_sz = total_sz;
+ e_hdr.headers_sz = 512;
+ e_hdr.subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
+ e_hdr.rva_and_sizes_nr = 1;
+ fwrite(&e_hdr, sizeof(e_hdr), 1, f);
+
+ memset(&t_sec, 0, sizeof(t_sec));
+ strcpy((char *)t_sec.name, ".text");
+ t_sec.virtual_sz = total_sz;
+ t_sec.raw_data_sz = total_sz;
+ t_sec.characteristics = IMAGE_SCN_CNT_CODE |
+ IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
+ IMAGE_SCN_MEM_READ;
+ fwrite(&t_sec, sizeof(t_sec), 1, f);
+
+ /*
+ * Write our dummy relocation and reloc section.
+ */
+ memset(&r_sec, 0, sizeof(r_sec));
+ strcpy((char *)r_sec.name, ".reloc");
+ r_sec.virtual_sz = sizeof(c_rel);
+ r_sec.virtual_address = ftell(f) + sizeof(r_sec);
+ r_sec.raw_data_sz = r_sec.virtual_sz;
+ r_sec.raw_data = r_sec.virtual_address;
+ r_sec.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
+ IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_DISCARDABLE |
+ IMAGE_SCN_MEM_READ;
+ fwrite(&r_sec, sizeof(r_sec), 1, f);
+
+ memset(&c_rel, 0, sizeof(c_rel));
+ c_rel.virtual_address = ftell(f) + sizeof(c_rel);
+ c_rel.symtab_index = 10;
+ fwrite(&c_rel, sizeof(c_rel), 1, f);
+ fwrite(&dummy, sizeof(dummy), 1, f);
+
+}
+
+static void usage(char *progname)
+{
+ fprintf(stderr, "usage: %s <ELF shared object> <output file>\n",
+ progname);
+}
+
+int main(int argc, char **argv)
+{
+ struct stat st;
+ Elf32_Ehdr e_hdr;
+ FILE *f_in, *f_out;
+ void *buf;
+ size_t rv;
+
+ if (argc < 3) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ f_in = fopen(argv[1], "r");
+ if (!f_in) {
+ perror("fopen");
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(argv[1], &st) != 0) {
+ perror("stat");
+ exit(EXIT_FAILURE);
+ }
+
+ f_out = fopen(argv[2], "w");
+ if (!f_out) {
+ perror("fopen");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Parse the ELF header and find the entry point.
+ */
+ fread((void *)&e_hdr, sizeof(e_hdr), 1, f_in);
+ if (e_hdr.e_ident[EI_MAG0] != ELFMAG0 ||
+ e_hdr.e_ident[EI_MAG1] != ELFMAG1 ||
+ e_hdr.e_ident[EI_MAG2] != ELFMAG2 ||
+ e_hdr.e_ident[EI_MAG3] != ELFMAG3) {
+ fprintf(stderr, "Input file not ELF shared object\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* We only support 32-bit for now.. */
+ if (e_hdr.e_ident[EI_CLASS] != ELFCLASS32) {
+ fprintf(stderr, "Input file not 32-bit ELF shared object\n");
+ exit(EXIT_FAILURE);
+ }
+
+ buf = malloc(st.st_size);
+ if (!buf) {
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+
+ write_header(f_out, e_hdr.e_entry, st.st_size);
+
+ /* Write out the entire ELF shared object */
+ rewind(f_in);
+ rv = fread(buf, st.st_size, 1, f_in);
+ if (!rv && ferror(f_in)) {
+ fprintf(stderr, "Failed to read all bytes from input\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fwrite(buf, st.st_size, rv, f_out);
+ return 0;
+}
diff --git a/efi/wrapper.h b/efi/wrapper.h
new file mode 100644
index 00000000..c5f64a3c
--- /dev/null
+++ b/efi/wrapper.h
@@ -0,0 +1,112 @@
+#ifndef EFI_WRAPPER_H
+#define EFI_WRAPPER_H
+
+#define MSDOS_SIGNATURE 0x5a4d
+#define PE_SIGNATURE 0x4550
+#define PE32_FORMAT 0x10b
+
+#define IMAGE_FILE_MACHINE_I386 0x14c
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
+#define IMAGE_FILE_LINE_NUMBERS_STRIPPED 0x0004
+#define IMAGE_FILE_32BIT_MACHINE 0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
+
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 0x0a
+
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+
+#define __packed __attribute__((packed))
+#define OFFSETOF(t,m) ((size_t)&((t *)0)->m)
+
+struct header {
+ __uint16_t msdos_signature;
+ __uint8_t _pad1[0x3c - 2];
+ __uint32_t pe_hdr;
+ __uint16_t pe_signature;
+ __uint16_t _pad2;
+} __packed;
+
+/*
+ * COFF header
+ */
+struct coff_hdr {
+ __uint16_t arch;
+ __uint16_t nr_sections;
+ __uint32_t timedatestamp;
+ __uint32_t symtab;
+ __uint32_t nr_syms;
+ __uint16_t optional_hdr_sz;
+ __uint16_t characteristics;
+} __packed;
+
+struct optional_hdr {
+ __uint16_t format;
+ __uint8_t major_linker_version;
+ __uint8_t minor_linker_version;
+ __uint32_t code_sz;
+ __uint32_t initialized_data_sz;
+ __uint32_t uninitialized_data_sz;
+ __uint32_t entry_point;
+ __uint32_t base_code;
+ __uint32_t data;
+} __packed;
+
+/*
+ * Extra header fields
+ */
+struct extra_hdr {
+ __uint32_t image_base;
+ __uint32_t section_align;
+ __uint32_t file_align;
+ __uint16_t major_os_version;
+ __uint16_t minor_os_version;
+ __uint16_t major_image_version;
+ __uint16_t minor_image_version;
+ __uint16_t major_subsystem_version;
+ __uint16_t minor_subsystem_version;
+ __uint32_t win32_version;
+ __uint32_t image_sz;
+ __uint32_t headers_sz;
+ __uint32_t checksum;
+ __uint16_t subsystem;
+ __uint16_t dll_characteristics;
+ __uint32_t stack_reserve_sz;
+ __uint32_t stack_commit_sz;
+ __uint32_t heap_reserve_sz;
+ __uint32_t heap_commit_sz;
+ __uint32_t loader_flags;
+ __uint32_t rva_and_sizes_nr;
+ __uint64_t export_table;
+ __uint64_t import_table;
+ __uint64_t resource_table;
+ __uint64_t exception_table;
+ __uint64_t certification_table;
+ __uint64_t base_relocation_table;
+} __packed;
+
+struct section {
+ __uint8_t name[8];
+ __uint32_t virtual_sz;
+ __uint32_t virtual_address;
+ __uint32_t raw_data_sz;
+ __uint32_t raw_data;
+ __uint32_t relocs;
+ __uint32_t line_numbers;
+ __uint16_t relocs_nr;
+ __uint16_t line_numbers_nr;
+ __uint32_t characteristics;
+} __packed;
+
+struct coff_reloc {
+ __uint32_t virtual_address;
+ __uint32_t symtab_index;
+ __uint16_t type;
+};
+
+#endif /* EFI_WRAPPER_H */
diff --git a/mk/efi.mk b/mk/efi.mk
new file mode 100644
index 00000000..c6c93a2c
--- /dev/null
+++ b/mk/efi.mk
@@ -0,0 +1,37 @@
+include $(MAKEDIR)/syslinux.mk
+
+com32 = $(topdir)/com32
+core = $(topdir)/core
+
+ARCH=ia32
+LIBDIR=/usr/lib
+FORMAT=efi-app-$(ARCH)
+
+CFLAGS = -I/usr/include/efi -I/usr/include/efi/$(ARCH) \
+ -DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \
+ -Wall -I$(com32)/include -I$(core)/include -m32 -march=i686 \
+ -I$(com32)/lib/ -std=gnu99
+
+# gnuefi sometimes installs these under a gnuefi/ directory, and sometimes not
+CRT0 := $(shell find $(LIBDIR) -name crt0-efi-$(ARCH).o 2>/dev/null | tail -n1)
+LDSCRIPT := $(shell find $(LIBDIR) -name elf_$(ARCH)_efi.lds 2>/dev/null | tail -n1)
+
+LDFLAGS = -T $(LDSCRIPT) -Bsymbolic -shared -nostdlib -znocombreloc \
+ -L$(LIBDIR) $(CRT0)
+
+SFLAGS = $(GCCOPT) $(GCCWARN) -march=i386 \
+ -fomit-frame-pointer -D__COM32__ \
+ -nostdinc -iwithprefix include \
+ -I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE)
+
+.PRECIOUS: %.o
+%.o: %.S
+ $(CC) $(SFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.o
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+%.efi: %.so
+ $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
+ -j .rela -j .reloc --target=$(FORMAT) $*.so $@