2018-01-18 22:01:42 +00:00
|
|
|
#!/usr/bin/perl
|
|
|
|
# Create a UEFI "Firmware File" (FFS) with optional features.
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
2018-01-20 20:59:38 +00:00
|
|
|
use FindBin;
|
|
|
|
use lib "$FindBin::Bin/../lib";
|
2018-01-18 22:01:42 +00:00
|
|
|
use Getopt::Long;
|
|
|
|
use Digest::SHA 'sha1';
|
2018-01-21 03:54:16 +00:00
|
|
|
use EFI;
|
2018-01-18 22:01:42 +00:00
|
|
|
|
2018-02-02 13:40:14 +00:00
|
|
|
my $usage = <<"END";
|
2018-01-18 22:01:42 +00:00
|
|
|
Usage:
|
|
|
|
$0 -o output.ffs [options] file.efi [...]
|
|
|
|
Options:
|
2018-02-02 13:40:14 +00:00
|
|
|
-h | -? | --help This help
|
2018-01-18 22:01:42 +00:00
|
|
|
-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)
|
2018-01-19 14:55:21 +00:00
|
|
|
-z | --compress Enable LZMA compression
|
2018-02-01 23:28:16 +00:00
|
|
|
-a | --auto Detect the section types from the filenames
|
2018-02-02 13:40:14 +00:00
|
|
|
END
|
2018-01-18 22:01:42 +00:00
|
|
|
|
|
|
|
my $output = '-';
|
|
|
|
my $name;
|
2018-02-01 23:28:16 +00:00
|
|
|
my $type;
|
2018-01-18 22:01:42 +00:00
|
|
|
my $version;
|
|
|
|
my $guid;
|
|
|
|
my $depex;
|
2018-01-19 14:55:21 +00:00
|
|
|
my $compress;
|
2018-02-01 23:28:16 +00:00
|
|
|
my $auto;
|
2018-01-18 22:01:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
GetOptions(
|
2018-02-02 13:40:14 +00:00
|
|
|
"h|?|help" => sub { print $usage; exit 0 },
|
2018-01-18 22:01:42 +00:00
|
|
|
"o|output=s" => \$output,
|
|
|
|
"n|name=s" => \$name,
|
|
|
|
"t|type=s" => \$type,
|
|
|
|
"v|version=s" => \$version,
|
|
|
|
"g|guid=s" => \$guid,
|
|
|
|
"d|depex=s" => \$depex,
|
2018-01-19 14:55:21 +00:00
|
|
|
"z|compress+" => \$compress,
|
2018-02-01 23:28:16 +00:00
|
|
|
"a|auto+" => \$auto,
|
2018-01-18 22:01:42 +00:00
|
|
|
) or die $usage;
|
|
|
|
|
2018-01-21 03:54:16 +00:00
|
|
|
die "$type: unknown file type\n"
|
2018-02-01 23:28:16 +00:00
|
|
|
if $type and not exists $EFI::file_types{$type};
|
2018-01-18 22:01:42 +00:00
|
|
|
|
2018-02-02 13:40:14 +00:00
|
|
|
die "At least one FFS section is required\n" unless @ARGV;
|
|
|
|
|
2018-01-22 20:43:40 +00:00
|
|
|
my @sections;
|
2018-01-18 22:01:42 +00:00
|
|
|
|
|
|
|
# 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
|
2018-01-20 22:28:50 +00:00
|
|
|
# The file goes first so that it will be aligned correctly
|
|
|
|
# on the 4 KB page inside the FV.
|
2018-01-18 22:01:42 +00:00
|
|
|
local $/ = undef;
|
|
|
|
while(<>)
|
|
|
|
{
|
2018-02-01 23:28:16 +00:00
|
|
|
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', $_);
|
2018-01-18 22:01:42 +00:00
|
|
|
}
|
|
|
|
|
2018-01-20 22:28:50 +00:00
|
|
|
# Put the optional parts after the input data
|
2018-01-22 20:43:40 +00:00
|
|
|
push @sections, EFI::section(USER_INTERFACE => EFI::ucs16($name))
|
2018-01-20 22:28:50 +00:00
|
|
|
if $name;
|
|
|
|
|
2018-01-22 20:43:40 +00:00
|
|
|
push @sections, EFI::section(VERSION => EFI::ucs16(chr(0x00) . $version))
|
2018-01-20 22:28:50 +00:00
|
|
|
if $version;
|
|
|
|
|
2018-01-22 20:43:40 +00:00
|
|
|
push @sections, EFI::depex($type, split /\s+/, $depex)
|
2018-01-20 22:28:50 +00:00
|
|
|
if $depex;
|
|
|
|
|
2018-01-18 22:01:42 +00:00
|
|
|
|
2018-01-22 20:43:40 +00:00
|
|
|
# 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;
|
|
|
|
|
2018-01-18 22:01:42 +00:00
|
|
|
# If no GUID was provided, make one from the name
|
|
|
|
# if there is no name from the data
|
|
|
|
if ($guid)
|
|
|
|
{
|
2018-01-21 03:54:16 +00:00
|
|
|
$guid = EFI::guid($guid);
|
2018-01-18 22:01:42 +00:00
|
|
|
}
|
2018-01-22 20:43:40 +00:00
|
|
|
elsif ($name)
|
|
|
|
{
|
|
|
|
# Generate a deterministic GUID based on the UI name
|
|
|
|
$guid = substr(sha1($name), 0, 16);
|
|
|
|
}
|
2018-01-18 22:01:42 +00:00
|
|
|
|
2018-01-21 03:54:16 +00:00
|
|
|
# Create the FFS header for the sections
|
2018-02-01 23:28:16 +00:00
|
|
|
my $ffs = EFI::ffs($type || 'FREEFORM', $guid, @sections);
|
2018-01-18 22:01:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
if ($output eq '-')
|
|
|
|
{
|
|
|
|
print $ffs;
|
|
|
|
} else {
|
2018-01-18 22:22:01 +00:00
|
|
|
open OUTPUT, ">", $output
|
2018-01-18 22:01:42 +00:00
|
|
|
or die "$output: Unable to open: $!\n";
|
|
|
|
|
|
|
|
print OUTPUT $ffs;
|
|
|
|
close OUTPUT;
|
|
|
|
}
|