fvloader and hello world example dxe modules

This commit is contained in:
Trammell hudson 2018-04-16 14:13:43 -04:00 committed by Trammell Hudson
parent f0ea0af860
commit c888af548a
Failed to extract signature
9 changed files with 563 additions and 0 deletions

53
dxe/Makefile Normal file
View File

@ -0,0 +1,53 @@
KERNEL = $(shell uname -s)
CC = $(CROSS)gcc
TARGETS += fvloader.ffs
TARGETS += hello.ffs
all: $(TARGETS)
clean: FORCE
$(RM) *.efi *.exe *.rom *.o .*.d $(TARGETS)
FORCE:
%.efi: %.exe
CROSS=$(CROSS) \
./efi-wrap $< > $@
%.ffs: %.efi
../bin/create-ffs \
-o $@ \
-t DRIVER \
-n "$(basename $@)" \
$<
BITS=64
EFI_ARCH=x86_64
#BITS=32
#EFI_ARCH=x86
%.exe: %.o
$(CROSS)ld \
-T pei-x86-$(BITS).lds \
-o $@ \
$^
CFLAGS += \
-std=c99 \
-D__efi__ \
-nostdinc \
-fshort-wchar \
-mno-red-zone \
-fno-stack-protector \
-m$(BITS) \
-fpic \
-O3 \
-W \
-Wall \
-I . \
-MMD \
-MF .$(notdir $@).d \
-include .*.d

84
dxe/README.md Normal file
View File

@ -0,0 +1,84 @@
Overview
===
These are sample EFI Option ROMs that can be installed in Apple's
Thunderbolt Gigabit Ethernet adapter. Anything that does printouts
to the console device will only be visible if you have set the
`bootargs` NVRAM parameter to verbose mode:
sudo nvram bootargs=-v
Once you have build the `hello.rom` file, install it with the `b57tool`.
Unlike Broadcom's `B57UDAIG.EXE`, this does not require rebooting to DOS
and works with a hot-plugged Thunderbolt device:
sudo ../tools/b57tool --pxe hello.rom
This tool does require that you have installed the `DirectHW.kext` from
the CoreBoot project.
Developing ROMs
===
Calling conventions
---
The EFI environment uses the Microsoft ABI, so gcc must be told which
functions are called from or call into the EFI system. This is done
with the `EFIAPI` macro, which annotates the functions with the gcc
x86 extension [`__attribute__((ms_abi))`](https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#x86-Function-Attributes)
The entry point into the Option ROM must be named `efi_main()` and
should have the prototype:
EFI_STATUS
EFIAPI
efi_main(
EFI_HANDLE image,
EFI_SYSTEM_TABLE * st
);
Any callbacks that are registered, such as for the `ExitBootServices` event,
must also be flagged with `EFIAPI`.
Console I/O
---
It is possible to print to the screen while EFI is running. The
`EFI_SYSTEM_TABLE` struct has a `SIMPLE_TEXT_OUTPUT_INTERFACE` pointer `ConOut`
to a struct with an `OutputString()` function pointer. This function takes
UCS-2 wide characters:
st->ConOut->OutputString(st->ConOut, L"Hello, world!\n");
The screen and the console will likely not be setup when `efi_main()` is
called, so it is typical to register a callback for `ExitBootServices`
to do any output.
As noted above, it is necessary to boot the system in "verbose" mode to
see any output from EFI. Set the `bootargs` NVRAM variable to configure this:
sudo nvram bootargs=-v
Reading keystrokes for a shell or similar can be done with the `ConIn`
struct. I have not figured out how to use it, but I have figured out how
to hook it to read key strokes. See `roms/keylogger.c` for an example.
Memory allocation
---
There are lots of pools of memory allocation during EFI, some of which are
cleared when the OS starts, some of which stay resident, etc. In general
you can request memory with:
void * buf;
if (gST->BootServices->AllocatePool(
EfiBootServicesData,
len,
&buf
) != 0) {
// handle an error...
}

115
dxe/efi-wrap Executable file
View File

@ -0,0 +1,115 @@
#!/usr/bin/perl
# Convert a gcc/mingw compiled application to a valid EFI executable
# by reuseing a valid header. The linked doesn't generate the right
# fields for some reason.
#
# This is a total hack and should not be used by anything real.
# We really should figure out how to make this work the right way.
#
# 2015-04-21
use warnings;
use strict;
my $code_size_offset = 0x9C;
my $data_size_offset = 0xA0;
my $entry_offset = 0xA8;
my $code_offset = 0xAC;
my $CROSS = $ENV{CROSS} || "";
my $hdr = undump(<DATA>);
my $file = shift;
my $input = `${CROSS}objcopy -O binary "$file" "/tmp/$$.bin"; cat "/tmp/$$.bin"`;
my $header = `${CROSS}readelf -h "$file"`;
my $len = length $input;
my ($entry) = $header =~ /Entry point.*?(0x[0-9a-f]*)$/msg
or die "$file: Unable to parse entry point\n";
$entry = hex($entry) - 0x1e0;
my $code_start = 0;
my $code_size = length($input);
my $data_size = 0;
sub undump
{
my $bin;
for (@_)
{
my $bytes = substr($_, 9, 16*3);
$bin .= join '', map { chr(hex $_) } split / /, $bytes;
}
return $bin;
}
sub offset
{
my $offset = shift;
return unpack("L", substr($input, $offset, 4));
}
#my $entry = offset($entry_offset);
#my $code_start = offset($code_offset);
#my $code_size = offset($code_size_offset);
#my $data_size = offset($data_size_offset);
my $entry_hex = sprintf "%04x", $entry;
warn sprintf "Read %d bytes; entry %04x, code %04x @ %04x, data %04x\n",
$len,
$entry,
$code_start,
$code_size,
$data_size,
;
# fixup the entry point based on the difference in the code start values
$entry = ($entry - $code_start) + length($hdr);
warn sprintf "New entry: %04x\n", $entry;
my $img = $hdr . substr($input, $code_start);
substr($img, $entry_offset, 4) = pack("L", $entry);
print $img;
__END__
0000000: 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 MZ..............
0000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000030: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ................
0000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000080: 50 45 00 00 64 86 04 00 00 00 00 00 a0 e5 00 00 PE..d...........
0000090: 00 00 00 00 f0 00 0e 03 0b 02 02 38 00 00 00 00 ...........8....
00000a0: 00 00 00 00 00 00 00 00 7f 8e 00 00 40 02 00 00 ............@...
00000b0: 00 00 00 00 00 00 00 00 20 00 00 00 20 00 00 00 ........ ... ...
00000c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000d0: a0 e5 00 00 40 02 00 00 4f aa 01 00 0b 00 00 00 ....@...O.......
00000e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000100: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
0000110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ....H...`...1...
0000140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000180: 00 00 00 00 00 00 00 00 2e 74 65 78 74 00 00 00 .........text...
0000190: a0 db 00 00 40 02 00 00 a0 db 00 00 40 02 00 00 ....@.......@...
00001a0: 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..`
00001b0: 2e 64 61 74 61 00 00 00 20 07 00 00 e0 dd 00 00 .data... .......
00001c0: e0 02 00 00 e0 dd 00 00 00 00 00 00 00 00 00 00 ................
00001d0: 00 00 00 00 60 00 00 e0 2e 72 65 6c 6f 63 00 00 ....`....reloc..
00001e0: 48 00 00 00 00 e5 00 00 60 00 00 00 00 e5 00 00 H.......`.......
00001f0: 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 62 ............`..b
0000200: 2e 64 65 62 75 67 00 00 31 00 00 00 60 e5 00 00 .debug..1...`...
0000210: 40 00 00 00 60 e5 00 00 00 00 00 00 00 00 00 00 @...`...........
0000220: 00 00 00 00 60 00 00 62 00 00 00 00 00 00 00 00 ....`..b........
0000230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

110
dxe/efi.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef __efi_h__
#define __efi_h__
/* Just enough of the EFI API to write some code */
#define EFIAPI __attribute__((ms_abi))
#define NULL 0
typedef int EFI_STATUS;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef unsigned short wchar_t;
typedef void * EFI_HANDLE;
typedef struct {
uint64_t Signature;
uint32_t Revision;
uint32_t HeaderSize;
uint32_t CRC32;
uint32_t Reserved;
} EFI_TABLE_HEADER;
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} EFI_GUID;
typedef struct {
EFI_TABLE_HEADER Hdr;
void* AddMemorySpace;
void* AllocateMemorySpace;
void* FreeMemorySpace;
void* RemoveMemorySpace;
void* GetMemorySpaceDescriptor;
void* SetMemorySpaceAttributes;
void* GetMemorySpaceMap;
void* AddIoSpace;
void* AllocateIoSpace;
void* FreeIoSpace;
void* RemoveIoSpace;
void* GetIoSpaceDescriptor;
void* GetIoSpaceMap;
void* Dispatch;
void* Schedule;
void* Trust;
EFI_STATUS EFIAPI (*ProcessFirmwareVolume)(
void * buffer,
unsigned len,
EFI_HANDLE * handle_out
);
} EFI_DXE_SERVICES;
typedef struct {
EFI_GUID VendorGuid;
void *VendorTable;
} EFI_CONFIGURATION_TABLE;
typedef struct {
EFI_TABLE_HEADER Hdr;
wchar_t *FirmwareVendor;
uint32_t FirmwareRevision;
void* ConsoleInHandle;
void* *ConIn;
void* ConsoleOutHandle;
void* *ConOut;
void* StandardErrorHandle;
void* *StdErr;
void* *RuntimeServices;
void* *BootServices;
unsigned NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
static inline void *
efi_find_table(
EFI_SYSTEM_TABLE * st,
uint32_t search_guid
)
{
const EFI_CONFIGURATION_TABLE * ct = st->ConfigurationTable;
serial_string("num tables=");
serial_hex(st->NumberOfTableEntries, 4);
for(unsigned i = 0 ; i < st->NumberOfTableEntries ; i++)
{
const EFI_GUID * guid = &ct[i].VendorGuid;
serial_hex(*(uint64_t*)guid, 16);
if (guid->Data1 == search_guid)
return ct[i].VendorTable;
}
return NULL;
}
#endif

53
dxe/fvloader.c Normal file
View File

@ -0,0 +1,53 @@
/** \file
* Tell DxeCore about an alternate firmware volume in the ROM.
*
* This allows LinuxBoot to locate the Linux kernel and initrd
* outside of the normal DXE volume, which is quite small on some
* systems.
*/
// #define VOLUME_ADDRESS 0xFF840000 // Winterfell
// #define VOLUME_LENGTH 0x20000
// Heron
#define VOLUME_ADDRESS 0xFF410000
#define VOLUME_LENGTH 0x00800000
#include "serial.h"
#include "efi.h"
EFI_STATUS
EFIAPI
efi_main(
EFI_HANDLE image,
EFI_SYSTEM_TABLE * const st
)
{
(void) image;
//gST = st;
//gBS = gST->BootServices;
//gRT = gST->RuntimeServices;
const EFI_DXE_SERVICES * dxe_services = efi_find_table(st, 0x5ad34ba);
if (!dxe_services)
{
serial_string("FvLoader: No DXE system table found...\r\n");
return 0x80000001;
}
serial_string("FvLoader: adding firmware volume 0x");
serial_hex(VOLUME_ADDRESS, 8);
EFI_HANDLE handle;
int rc = dxe_services->ProcessFirmwareVolume(
(void*) VOLUME_ADDRESS,
VOLUME_LENGTH,
&handle
);
serial_string("FvLoader: rc="); serial_hex(rc, 8);
return rc;
}

22
dxe/hello.c Normal file
View File

@ -0,0 +1,22 @@
/** \file
*/
#include "serial.h"
#include "efi.h"
EFI_STATUS
EFIAPI
efi_main(
EFI_HANDLE image,
EFI_SYSTEM_TABLE * const st
)
{
(void) image;
(void) st;
serial_string("+---------------+\r\n");
serial_string("| Hello, world! |\r\n");
serial_string("+---------------+\r\n");
return 0;
}

30
dxe/pei-x86-32.lds Normal file
View File

@ -0,0 +1,30 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
/*OUTPUT_FORMAT("pei-x86-64", "pei-x86-64", "pei-x86-64") */
OUTPUT_ARCH(i386)
ENTRY(efi_main)
SECTIONS
{
/* 0x1e0 is the size of the MZ header that will be added by gcc */
.text 0x1e0 :
{
*(.text)
}
.data :
{
*(.rodata*)
*(.data*)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
*(.bss)
}
/DISCARD/ :
{
*(.xdata*)
*(.idata*)
*(.pdata*)
*(.comment)
*(.eh_fram*)
}
}

30
dxe/pei-x86-64.lds Normal file
View File

@ -0,0 +1,30 @@
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
/*OUTPUT_FORMAT("pei-x86-64", "pei-x86-64", "pei-x86-64") */
OUTPUT_ARCH(i386:x86-64)
ENTRY(efi_main)
SECTIONS
{
/* 0x1e0 is the size of the MZ header that will be added by gcc */
.text 0x1e0 :
{
*(.text)
}
.data :
{
*(.rodata*)
*(.data*)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
*(.bss)
}
/DISCARD/ :
{
*(.xdata*)
*(.idata*)
*(.pdata*)
*(.comment)
*(.eh_fram*)
}
}

66
dxe/serial.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef __serial_h__
#define __serial_h__
static __inline void
outb (unsigned char __value, unsigned short int __port)
{
__asm__ __volatile__ ("outb %b0,%w1": :"a" (__value), "Nd" (__port));
}
static __inline unsigned char
inb (unsigned short int __port)
{
unsigned char _v;
__asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (__port));
return _v;
}
#define PORT 0x3f8 /* COM1 */
#define DLAB 0x80
#define TXR 0 /* Transmit register (WRITE) */
#define RXR 0 /* Receive register (READ) */
#define IER 1 /* Interrupt Enable */
#define IIR 2 /* Interrupt ID */
#define FCR 2 /* FIFO control */
#define LCR 3 /* Line control */
#define MCR 4 /* Modem control */
#define LSR 5 /* Line Status */
#define MSR 6 /* Modem Status */
#define DLL 0 /* Divisor Latch Low */
#define DLH 1 /* Divisor latch High */
static int is_transmit_empty()
{
return inb(PORT + 5) & 0x20;
}
static void serial_char(char a) {
outb(a, PORT);
while (is_transmit_empty() == 0);
}
static void serial_string(const char * s)
{
while(*s)
serial_char(*s++);
}
static void serial_hex(unsigned long x, unsigned digits)
{
while(digits-- > 0)
{
unsigned d = (x >> (digits * 4)) & 0xF;
if (d >= 0xA)
serial_char(d + 'A' - 0xA);
else
serial_char(d + '0');
}
serial_char('\r');
serial_char('\n');
}
#endif