[efi] Compress EFI ROM images

Use the reference implementation of the EFI compression algorithm
(taken from the EDK2 codebase, with minor bugfixes to allow
compilation with -Werror) to compress EFI ROM images.

Inspired-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/268/head
Michael Brown 2021-02-19 19:58:04 +00:00
parent 900f1f98d3
commit b76281a885
4 changed files with 1652 additions and 6 deletions

View File

@ -43,7 +43,7 @@ $(BIN)/%.drv.efi : $(BIN)/%.efidrv
$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@
$(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) -c $< $@
$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined
$(QM)$(ECHO) " [CAB] $@"

View File

@ -1425,7 +1425,7 @@ $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS)
$(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -DEFI_TARGET64 $< -o $@
CLEANUP += $(ELF2EFI64)
$(EFIROM) : util/efirom.c $(MAKEDEPS)
$(EFIROM) : util/efirom.c util/eficompress.c $(MAKEDEPS)
$(QM)$(ECHO) " [HOSTCC] $@"
$(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $<
CLEANUP += $(EFIROM)

File diff suppressed because it is too large Load Diff

View File

@ -34,10 +34,17 @@
#define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
/* Round up ROM size */
#define ROM_SIZE( len ) ( ( (len) + 511 ) & ~511 )
/* Include the EDK2 compression code */
#include "eficompress.c"
/** Command-line options */
struct options {
uint16_t vendor;
uint16_t device;
int compress;
};
/**
@ -94,6 +101,35 @@ static void read_pe_info ( void *pe, uint16_t *machine,
}
}
/**
* Attempt to compress EFI data in-place
*
* @v data Data to be compressed
* @v max_len Length of data
* @ret len Length after attempted compression
*/
static size_t efi_compress ( void *data, size_t max_len ) {
void *tmp;
UINT32 len;
/* Allocate temporary buffer for compressed data */
tmp = xmalloc ( max_len );
/* Attempt compression */
len = max_len;
if ( ( EfiCompress ( data, max_len, tmp, &len ) == 0 ) &&
( len < max_len ) ) {
memcpy ( data, tmp, len );
} else {
len = max_len;
}
/* Free temporary buffer */
free ( tmp );
return len;
}
/**
* Convert EFI image to ROM image
*
@ -109,10 +145,14 @@ static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
struct stat pe_stat;
size_t pe_size;
size_t rom_size;
size_t compressed_size;
void *buf;
void *payload;
unsigned int i;
uint16_t machine;
uint16_t subsystem;
uint8_t checksum;
int compressed;
/* Determine PE file size */
if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) {
@ -123,7 +163,7 @@ static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
pe_size = pe_stat.st_size;
/* Determine ROM file size */
rom_size = ( ( pe_size + sizeof ( *headers ) + 511 ) & ~511 );
rom_size = ROM_SIZE ( sizeof ( *headers ) + pe_size );
/* Allocate ROM buffer and read in PE file */
buf = xmalloc ( rom_size );
@ -136,12 +176,26 @@ static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
exit ( 1 );
}
/* Parse PE headers */
read_pe_info ( payload, &machine, &subsystem );
/* Compress the image, if requested */
if ( opts->compress ) {
compressed_size = efi_compress ( payload, pe_size );
rom_size = ROM_SIZE ( sizeof ( *headers ) + compressed_size );
compressed = ( compressed_size < pe_size );
} else {
compressed = 0;
}
/* Construct ROM header */
headers->rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE;
headers->rom.InitializationSize = ( rom_size / 512 );
headers->rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE;
read_pe_info ( payload, &headers->rom.EfiMachineType,
&headers->rom.EfiSubsystem );
headers->rom.EfiSubsystem = subsystem;
headers->rom.EfiMachineType = machine;
headers->rom.CompressionType =
( compressed ? EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED : 0 );
headers->rom.EfiImageHeaderOffset = sizeof ( *headers );
headers->rom.PcirOffset =
offsetof ( typeof ( *headers ), pci );
@ -194,11 +248,12 @@ static int parse_options ( const int argc, char **argv,
static struct option long_options[] = {
{ "vendor", required_argument, NULL, 'v' },
{ "device", required_argument, NULL, 'd' },
{ "compress", 0, NULL, 'c' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 }
};
if ( ( c = getopt_long ( argc, argv, "v:d:h",
if ( ( c = getopt_long ( argc, argv, "v:d:ch",
long_options,
&option_index ) ) == -1 ) {
break;
@ -219,6 +274,9 @@ static int parse_options ( const int argc, char **argv,
exit ( 2 );
}
break;
case 'c':
opts->compress = 1;
break;
case 'h':
print_help ( argv[0] );
exit ( 0 );