linuxboot/bin/create-ffs
2018-02-02 08:40:14 -05:00

144 lines
3.4 KiB
Perl
Executable File

#!/usr/bin/perl
# Create a UEFI "Firmware File" (FFS) with optional features.
use warnings;
use strict;
use FindBin;
use lib "$FindBin::Bin/../lib";
use Getopt::Long;
use Digest::SHA 'sha1';
use EFI;
my $usage = <<"END";
Usage:
$0 -o output.ffs [options] file.efi [...]
Options:
-h | -? | --help This help
-o | --output output.ffs Output file (default is stdout)
-n | --name FileName Name to include in UI Section
-t | --type Type FREEFORM|DRIVER|SMM|DXE_CORE|SMM_CORE|PEIM
-v | --version 1.0 Version section
-g | --guid GUID This file GUID (default is hash of Name)
-d | --depex 'guid guid..' Optional dependencies (all ANDed, or TRUE)
-z | --compress Enable LZMA compression
-a | --auto Detect the section types from the filenames
END
my $output = '-';
my $name;
my $type;
my $version;
my $guid;
my $depex;
my $compress;
my $auto;
GetOptions(
"h|?|help" => sub { print $usage; exit 0 },
"o|output=s" => \$output,
"n|name=s" => \$name,
"t|type=s" => \$type,
"v|version=s" => \$version,
"g|guid=s" => \$guid,
"d|depex=s" => \$depex,
"z|compress+" => \$compress,
"a|auto+" => \$auto,
) or die $usage;
die "$type: unknown file type\n"
if $type and not exists $EFI::file_types{$type};
die "At least one FFS section is required\n" unless @ARGV;
my @sections;
# Read entire files at a time and append a new section
# for each file read. Some special types have their own
# section type; otherwise we're adding a PE32
# The file goes first so that it will be aligned correctly
# on the 4 KB page inside the FV.
local $/ = undef;
while(<>)
{
my $sec;
if ($auto)
{
# attempt to determine the section type from the name
# of the file. This is useful for re-wrapping
# unpacked files from uefi-firmware-parser.
if ($ARGV =~ /\.dxe.depex$/)
{
$sec = 'DXE_DEPEX';
$type ||= 'DRIVER';
}
if ($ARGV =~ /\.smm.depex$/)
{
$sec = 'SMM_DEPEX';
$type ||= 'SMM';
}
if ($ARGV =~ /\.pie.depex$/)
{
$sec = 'PEI_DEPEX';
$type ||= 'PEIM';
}
$sec = 'PE32' if $ARGV =~ /\.pe$/;
$sec = 'RAW' if $ARGV =~ /\.raw$/;
$sec = 'VERSION' if $ARGV =~ /\.version$/;
$sec = 'USER_INTERFACE' if $ARGV =~ /\.ui$/;
$sec = 'FREEFORM_SUBTYPE_GUID' if $ARGV =~ /\.freeform.guid$/;
die "$ARGV: unknown auto extension\n"
unless $sec;
} else {
$sec = $EFI::section_type_map{$type};
}
push @sections, EFI::section($sec || 'PE32', $_);
}
# Put the optional parts after the input data
push @sections, EFI::section(USER_INTERFACE => EFI::ucs16($name))
if $name;
push @sections, EFI::section(VERSION => EFI::ucs16(chr(0x00) . $version))
if $version;
push @sections, EFI::depex($type, split /\s+/, $depex)
if $depex;
# If we're compressing, compress the data and wrap it with a GUIDed header
# replacing all of the existing sections.
@sections = ( EFI::compress(@sections) )
if $compress;
# If no GUID was provided, make one from the name
# if there is no name from the data
if ($guid)
{
$guid = EFI::guid($guid);
}
elsif ($name)
{
# Generate a deterministic GUID based on the UI name
$guid = substr(sha1($name), 0, 16);
}
# Create the FFS header for the sections
my $ffs = EFI::ffs($type || 'FREEFORM', $guid, @sections);
if ($output eq '-')
{
print $ffs;
} else {
open OUTPUT, ">", $output
or die "$output: Unable to open: $!\n";
print OUTPUT $ffs;
close OUTPUT;
}