diff options
author | Don Dugger <n0ano@n0ano.com> | 2016-06-03 03:33:22 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@172.30.200.206> | 2016-06-03 03:33:23 +0000 |
commit | da27230f80795d0028333713f036d44c53cb0e68 (patch) | |
tree | b3d379eaf000adf72b36cb01cdf4d79c3e3f064c /qemu/roms/ipxe/src/util | |
parent | 0e68cb048bb8aadb14675f5d4286d8ab2fc35449 (diff) | |
parent | 437fd90c0250dee670290f9b714253671a990160 (diff) |
Merge "These changes are the raw update to qemu-2.6."
Diffstat (limited to 'qemu/roms/ipxe/src/util')
-rw-r--r-- | qemu/roms/ipxe/src/util/Option/ROM.pm | 20 | ||||
-rwxr-xr-x | qemu/roms/ipxe/src/util/disrom.pl | 4 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/util/elf2efi.c | 6 | ||||
-rwxr-xr-x | qemu/roms/ipxe/src/util/licence.pl | 13 | ||||
-rwxr-xr-x | qemu/roms/ipxe/src/util/parserom.pl | 296 | ||||
-rwxr-xr-x | qemu/roms/ipxe/src/util/relicense.pl | 169 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/util/zbin.c | 96 |
7 files changed, 541 insertions, 63 deletions
diff --git a/qemu/roms/ipxe/src/util/Option/ROM.pm b/qemu/roms/ipxe/src/util/Option/ROM.pm index 6c396730e..232cf16b8 100644 --- a/qemu/roms/ipxe/src/util/Option/ROM.pm +++ b/qemu/roms/ipxe/src/util/Option/ROM.pm @@ -529,6 +529,26 @@ sub new { return $hash; } +sub device_list { + my $hash = shift; + my $self = tied(%$hash); + + my $device_list = $hash->{device_list}; + return undef unless $device_list; + + my @ids; + my $offset = ( $self->{offset} + $device_list ); + while ( 1 ) { + my $raw = substr ( ${$self->{data}}, $offset, 2 ); + my $id = unpack ( "S", $raw ); + last unless $id; + push @ids, $id; + $offset += 2; + } + + return @ids; +} + ############################################################################## # # Option::ROM::PnP diff --git a/qemu/roms/ipxe/src/util/disrom.pl b/qemu/roms/ipxe/src/util/disrom.pl index 574957acd..920a86b24 100755 --- a/qemu/roms/ipxe/src/util/disrom.pl +++ b/qemu/roms/ipxe/src/util/disrom.pl @@ -55,6 +55,10 @@ do { printf " %-16s %s\n", "Signature:", $pci->{signature}; printf " %-16s 0x%04x\n", "Vendor ID:", $pci->{vendor_id}; printf " %-16s 0x%04x\n", "Device ID:", $pci->{device_id}; + if ( $pci->{device_list} ) { + printf " %-16s %s\n", "Device list:", + ( join ( ", ", map { sprintf "0x%04x", $_ } $pci->device_list ) ); + } printf " %-16s 0x%02x%02x%02x\n", "Device class:", $pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf}; printf " %-16s 0x%04x (%d)\n", "Image length:", diff --git a/qemu/roms/ipxe/src/util/elf2efi.c b/qemu/roms/ipxe/src/util/elf2efi.c index 45d539574..e68fa5d14 100644 --- a/qemu/roms/ipxe/src/util/elf2efi.c +++ b/qemu/roms/ipxe/src/util/elf2efi.c @@ -478,11 +478,13 @@ static void process_reloc ( bfd *bfd __attribute__ (( unused )), /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ + } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { /* Generate an 8-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 8 ); - } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { + } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) { /* Generate a 4-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 4 ); } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { diff --git a/qemu/roms/ipxe/src/util/licence.pl b/qemu/roms/ipxe/src/util/licence.pl index 0e43c7b4c..79e70fd65 100755 --- a/qemu/roms/ipxe/src/util/licence.pl +++ b/qemu/roms/ipxe/src/util/licence.pl @@ -37,6 +37,7 @@ my $known_licences = { desc => "GPL version 2 (or, at your option, any later version)", can_subsume => { gpl_any => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -49,6 +50,7 @@ my $known_licences = { can_subsume => { gpl_any => 1, gpl2_or_later => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -56,6 +58,17 @@ my $known_licences = { isc => 1, }, }, + gpl2_or_later_or_ubdl => { + desc => ( "GPL version 2 (or, at your option, any later version) or ". + "Unmodified Binary Distribution Licence" ), + can_subsume => { + public_domain => 1, + bsd3 => 1, + bsd2 => 1, + mit => 1, + isc => 1, + }, + }, public_domain => { desc => "Public Domain", can_subsume => {}, diff --git a/qemu/roms/ipxe/src/util/parserom.pl b/qemu/roms/ipxe/src/util/parserom.pl index e278e6336..28df60652 100755 --- a/qemu/roms/ipxe/src/util/parserom.pl +++ b/qemu/roms/ipxe/src/util/parserom.pl @@ -1,66 +1,260 @@ #!/usr/bin/env perl # -# Parse PCI_ROM and ISA_ROM entries from a source file on stdin and -# output the relevant Makefile variable definitions to stdout +# Parse PCI_ROM and ISA_ROM entries from source file(s) specified as +# arguments and output the relevant Makefile rules to STDOUT. # -# Based upon portions of Ken Yap's genrules.pl +# Originally based on portions of Ken Yap's genrules.pl. Completely +# rewritten by Robin Smidsrød to be more maintainable. use strict; use warnings; +use Getopt::Long; -die "Syntax: $0 driver_source.c" unless @ARGV == 1; -my $source = shift; -open DRV, "<$source" or die "Could not open $source: $!\n"; +# Parse command-line options +my @exclude_driver_classes = (); +my @exclude_drivers = (); +my $debug = 0; +my $help = 0; +GetOptions( + "exclude-driver-class=s" => \@exclude_driver_classes, + "exclude-driver=s" => \@exclude_drivers, + "debug" => \$debug, + "help" => \$help, +); -( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ ) - or die "Could not parse source file name \"$source\"\n"; +# Convert exclution arrays to lookup tables +my $exclude_driver_class_map = { map { $_ => 1 } @exclude_driver_classes }; +my $exclude_driver_map = { map { $_ => 1 } @exclude_drivers }; -my $printed_family; +# Ensure STDOUT and STDERR are synchronized if debugging +if ( $debug ) { + STDOUT->autoflush(1); + STDERR->autoflush(1); +} + +# Compile regular expressions here for slight performance boost +my %RE = ( + 'parse_driver_class' => qr{ drivers/ (\w+?) / }x, + 'parse_family' => qr{^ (?:\./)? (.*) \..+? $}x, + 'find_rom_line' => qr/^ \s* ( (PCI|ISA)_ROM \s* \( \s* (.*?) ) $/x, + 'extract_pci_id' => qr/^ \s* 0x([0-9A-Fa-f]{4}) \s* ,? \s* (.*) $/x, + 'extract_quoted_string' => qr/^ \s* \" ([^\"]*?) \" \s* ,? \s* (.*) $/x, +); + +# Show help if required arguments are missing or help was requested +show_usage_and_exit() if $help or @ARGV < 1; + +# Process each source file specified +process_source_file($_) for @ARGV; + +exit; + +sub show_usage_and_exit { + print STDERR <<"EOM"; +Syntax: $0 [<options>] <source-file> [<source-file>] +Options: + --exclude-driver-class Exclude specified driver classes + --exclude-driver Exclude specified drivers + --debug Output debug information on STDERR + --help This help information +EOM + exit 1; +} + +# Figure out if source file is a driver and look for ROM declarations +sub process_source_file { + my ($source_file) = @_; + return unless defined $source_file; + return unless length $source_file; + my $state = { 'source_file' => $source_file }; + log_debug("SOURCE_FILE", $state->{source_file}); + # Skip source files that aren't drivers + parse_driver_class( $state ); + unless ( $state->{'driver_class'} ) { + log_debug("SKIP_NOT_DRIVER", $state->{source_file} ); + return; + } + # Skip source files with driver classes that are explicitly excluded + if ( $exclude_driver_class_map->{ $state->{'driver_class'} } ) { + log_debug("SKIP_EXCL_CLASS", $state->{'driver_class'} ); + return; + } + # Skip source files without driver information + parse_family( $state ); + parse_driver_name( $state ); + unless ( $state->{'family'} and $state->{'driver_name'} ) { + log_debug("SKIP_NO_DRV_INFO", $state->{source_file} ); + return; + } + # Skip source files with drivers that are explicitly excluded + if ( $exclude_driver_map->{ $state->{'driver_name'} } ) { + log_debug("SKIP_EXCL_DRV", $state->{'driver_name'} ); + return; + } + # Iterate through lines in source files looking for ROM declarations + # and # output Makefile rules + open( my $fh, "<", $state->{'source_file'} ) + or die "Couldn't open $state->{source_file}: $!\n"; + while (<$fh>) { + process_rom_decl($state, $1, $2, $3) if m/$RE{find_rom_line}/; + } + close($fh) or die "Couldn't close $source_file: $!\n"; + return 1; +} + +# Verify that the found ROM declaration is sane and dispatch to the right +# handler depending on type +sub process_rom_decl { + my ($state, $rom_line, $rom_type, $rom_decl) = @_; + return unless defined $rom_line; + return unless length $rom_line; + log_debug("ROM_LINE", $rom_line); + return unless defined $rom_type; + return unless length $rom_type; + log_debug("ROM_TYPE", $rom_type); + $state->{'type'} = lc $rom_type; + return process_pci_rom($state, $rom_decl) if $rom_type eq "PCI"; + return process_isa_rom($state, $rom_decl) if $rom_type eq "ISA"; + return; +} + +# Extract values from PCI_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_pci_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $vendor, $decl) = extract_pci_id($decl, 'PCI_VENDOR'); + (my $device, $decl) = extract_pci_id($decl, 'PCI_DEVICE'); + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $vendor and $device and $image and $desc ) { + print_make_rules( $state, "${vendor}${device}", $desc, $vendor, $device ); + print_make_rules( $state, $image, $desc, $vendor, $device, 1 ); + } + else { + log_debug("WARNING", "Malformed PCI_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} + +# Extract values from ISA_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_isa_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $image and $desc ) { + print_make_rules( $state, $image, $desc ); + } + else { + log_debug("WARNING", "Malformed ISA_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} -sub rom { - ( my $type, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; - my $ids = $vendor ? "$vendor,$device" : "-"; - unless ( $printed_family ) { +# Output Makefile rules for the specified ROM declarations +sub print_make_rules { + my ( $state, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; + unless ( $state->{'is_header_printed'} ) { + print "# NIC\t\n"; + print "# NIC\tfamily\t$state->{family}\n"; + print "DRIVERS_$state->{driver_class} += $state->{driver_name}\n"; + print "DRIVERS += $state->{driver_name}\n"; + print "\n"; + $state->{'is_header_printed'} = 1; + } + return if $vendor and ( $vendor eq "ffff" or $device eq "ffff" ); + my $ids = $vendor ? "$vendor,$device" : "-"; + print "# NIC\t$image\t$ids\t$desc\n"; + print "DRIVER_$image = $state->{driver_name}\n"; + print "ROM_TYPE_$image = $state->{type}\n"; + print "ROM_DESCRIPTION_$image = \"$desc\"\n"; + print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; + print "PCI_DEVICE_$image = 0x$device\n" if $device; + print "ROMS += $image\n" unless $dup; + print "ROMS_$state->{driver_name} += $image\n" unless $dup; print "\n"; - print "# NIC\t\n"; - print "# NIC\tfamily\t$family\n"; - print "DRIVERS += $driver_name\n"; - $printed_family = 1; - } - print "\n"; - return if ( $vendor && ( ( $vendor eq "ffff" ) || ( $device eq "ffff" ) ) ); - print "# NIC\t$image\t$ids\t$desc\n"; - print "DRIVER_$image = $driver_name\n"; - print "ROM_TYPE_$image = $type\n"; - print "ROM_DESCRIPTION_$image = \"$desc\"\n"; - print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; - print "PCI_DEVICE_$image = 0x$device\n" if $device; - print "ROMS += $image\n" unless $dup; - print "ROMS_$driver_name += $image\n" unless $dup; + return 1; +} + +# Driver class is whatever comes after the "drivers" part of the filename (relative path) +sub parse_driver_class { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_driver_class}/ ) { + log_debug("DRIVER_CLASS", $1); + $state->{'driver_class'} = $1; + } + return; +} + +# Family name is filename (relative path) without extension +sub parse_family { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_family}/ ) { + log_debug("FAMILY", $1); + $state->{'family'} = $1; + } + return; +} + +# Driver name is last part of family name +sub parse_driver_name { + my ($state) = @_; + my $family = $state->{'family'}; + return unless defined $family; + return unless length $family; + my @parts = split "/", $family; + $state->{'driver_name'} = $parts[-1]; + log_debug("DRIVER", $state->{'driver_name'}); + return; } -while ( <DRV> ) { - next unless /(PCI|ISA)_ROM\s*\(/; - - if ( /^\s*PCI_ROM\s*\( - \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor - \s*0x([0-9A-Fa-f]{4})\s*, # PCI device - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s*, # Description - \s*.*\s* # Driver data - \)/x ) { - ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 ); - rom ( "pci", lc "${vendor}${device}", $desc, $vendor, $device ); - rom ( "pci", $image, $desc, $vendor, $device, 1 ); - } elsif ( /^\s*ISA_ROM\s*\( - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s* # Description - \)/x ) { - ( my $image, my $desc ) = ( $1, $2 ); - rom ( "isa", $image, $desc ); - } else { - warn "Malformed PCI_ROM or ISA_ROM macro on line $. of $source\n"; - } +# Extract a PCI vendor/device ID e.g. 0x8086, possibly followed by a comma +# Should always be 4-digit lower-case hex number +sub extract_pci_id { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_pci_id}/ ) { + my $id = lc $1; + log_debug($label, $id); + return $id, $2; + } + return "", $str; } -close DRV; +# Extract a double-quoted string, possibly followed by a comma +sub extract_quoted_string { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_quoted_string}/ ) { + log_debug($label, $1); + return $1, $2; + } + return "", $str; +} + +# Output debug info to STDERR (off by default) +sub log_debug { + my ($label, $str) = @_; + return unless $debug; + return unless defined $str; + print STDERR "\n" if $label eq 'SOURCE_FILE'; + print STDERR "="; + if ( defined $label ) { + my $pad_count = 16 - length $label; + print STDERR $label . ":" . ( " " x $pad_count ); + } + print STDERR $str . "\n"; + return; +} diff --git a/qemu/roms/ipxe/src/util/relicense.pl b/qemu/roms/ipxe/src/util/relicense.pl new file mode 100755 index 000000000..41954c1b3 --- /dev/null +++ b/qemu/roms/ipxe/src/util/relicense.pl @@ -0,0 +1,169 @@ +#!/usr/bin/perl -w + +=head1 NAME + +relicense.pl + +=head1 SYNOPSIS + +relicense.pl [options] -p <permissions file> <file> [<file>...] + +Option: + + -p,--permitted=FILE Specify file of emails with relicensing permission + -f,--force Manually force relicensing + -h,--help Display brief help message + -v,--verbose Increase verbosity + -q,--quiet Decrease verbosity + +=cut + +use File::Slurp; +use IPC::Run qw ( run ); +use Getopt::Long; +use Pod::Usage; +use strict; +use warnings; + +# Parse command-line options +my $verbosity = 0; +my $permfile; +my $force; +Getopt::Long::Configure ( "bundling", "auto_abbrev" ); +GetOptions ( + 'permitted|p=s' => \$permfile, + 'force|f' => \$force, + 'verbose|v+' => sub { $verbosity++; }, + 'quiet|q+' => sub { $verbosity--; }, + 'help|h' => sub { pod2usage ( 1 ); }, +) or die "Could not parse command-line options"; +pod2usage ( 1 ) unless @ARGV; + +# Read permitted emails file +my @emails = ( $permfile ? read_file ( $permfile ) : () ); +chomp @emails; +my $permitted = { map { /^.*<(\S+)>$/; ( $1 || $_ ) => 1 } @emails }; + +# Define list of relicensable licences +my $relicensable = { + GPL2_OR_LATER => 1, +}; + +# Define blurb to be added to copyright notice +my $blurb = ' + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements.'; + +# Process files +my @succeeded; +my @failed; +while ( my $filename = shift @ARGV ) { + + # Read file to determine existing licence + my $file = read_file ( $filename ); + my @licences = ( $file =~ /^\s*FILE_LICENCE\s*\(\s*(\S+)\s*\)\s*;?$/mg ); + die "No licence declaration in $filename\n" unless @licences; + die "Multiple licence declarations in $filename\n" if @licences > 1; + my $licence = $licences[0]; + + # Skip if file is already UBDL-licensed + next if $licence =~ /_OR_UBDL$/; + + # Fail immediately if file is not a candidate for relicensing + if ( ! exists $relicensable->{$licence} ) { + print "Non-relicensable licence $licence in $filename\n"; + push @failed, $filename; + next; + } + + # Run git-blame + my $stdout; + my $stderr; + run [ "git", "blame", "-M", "-C", "-p", "-w", $filename ], + \undef, \$stdout, \$stderr + or die "git-blame $filename: $?"; + die $stderr if $stderr; + + # Process output + my @stdout = split ( /\n/, $stdout ); + chomp @stdout; + my $details = {}; + my $failures = 0; + while ( @stdout ) { + + # Parse output + my $commit_line = shift @stdout; + ( my $commit, undef, my $lineno, undef, my $count ) = + ( $commit_line =~ + /^([0-9a-f]{40})\s+([0-9]+)\s+([0-9]+)(\s+([0-9]+))?$/ ) + or die "Malformed commit line \"$commit_line\"\n"; + if ( $count ) { + $details->{$commit} ||= {}; + while ( ! ( $stdout[0] =~ /^\t/ ) ) { + my $detail_line = shift @stdout; + ( my $key, undef, my $value ) = + ( $detail_line =~ /^([a-z-]+)(\s+(.+))?$/ ) + or die "Malformed detail line \"$detail_line\" for $commit_line\n"; + $details->{$commit}->{$key} = $value; + } + } + die "Missing commit details for $commit_line\n" + unless %{$details->{$commit}}; + my $code_line = shift @stdout; + ( my $line ) = ( $code_line =~ /^\t(.*)$/ ) + or die "Malformed code line \"$code_line\" for $commit_line\n"; + + # Skip trivial lines and lines so common that they are likely to + # be misattributed by git-blame + next if $line =~ /^\s*$/; # Empty lines + next if $line =~ /^\s*\/\*/; # Start of comments + next if $line =~ /^\s*\*/; # Middle (or end) of comments + next if $line =~ /^\s*\{\s*$/; # Standalone opening braces + next if $line =~ /^\s*\};?\s*$/; # Standalone closing braces + next if $line =~ /^\#include/; # Header inclusions + next if $line =~ /^\s*return\s+0;/; # return 0; + next if $line =~ /^\s*return\s+rc;/; # return rc; + next if $line =~ /^\s*PCI_ROM\s*\(.*\)\s*,\s*$/; # PCI IDs + next if $line =~ /^\s*FILE_LICENCE\s*\(.*\)\s*;$/; # Licence declarations + + # Identify author + my $author_mail = $details->{$commit}->{"author-mail"} + or die "Missing author email for $commit_line\n"; + ( my $email ) = ( $author_mail =~ /^<(\S+)>$/ ) + or die "Malformed author email \"$author_mail\" for $commit_line\n"; + undef $email if exists $details->{$commit}->{boundary}; + + # Check for relicensing permission + next if defined $email && exists $permitted->{$email}; + + # Print out lines lacking permission + printf $filename."\n" unless $failures; + printf "%4d %-30s %s\n", $lineno, ( $email || "<root>" ), $line; + $failures++; + } + + # Fail if there are any non-trivial lines lacking relicensing permission + if ( $failures && ! $force ) { + push @failed, $filename; + next; + } + + # Modify FILE_LICENCE() line + $file =~ s/(^\s*FILE_LICENCE\s*\(\s*${licence})(\s*\)\s*;?$)/$1_OR_UBDL$2/m + or die "Could not modify FILE_LICENCE() in $filename\n"; + + # Modify copyright notice, if present + if ( $file =~ /GNU General Public License/i ) { + $file =~ s/(02110-1301, USA.$)/$1${blurb}/m + or die "Could not modify copyright notice in $filename\n"; + } + + # Write out modified file + write_file ( $filename, { atomic => 1 }, $file ); + push @succeeded, $filename; +} + +print "Relicensed: ".join ( " ", @succeeded )."\n" if @succeeded; +die "Cannot relicense: ".join ( " ", @failed )."\n" if @failed; diff --git a/qemu/roms/ipxe/src/util/zbin.c b/qemu/roms/ipxe/src/util/zbin.c index 3b7cf95b3..1862a3827 100644 --- a/qemu/roms/ipxe/src/util/zbin.c +++ b/qemu/roms/ipxe/src/util/zbin.c @@ -1,13 +1,21 @@ +#include <stdint.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> #include <sys/stat.h> - -#define ENCODE -#define VERBOSE -#include "nrv2b.c" -FILE *infile, *outfile; +#include <lzma.h> #define DEBUG 0 +/* LZMA filter choices. Must match those used by unlzma.S */ +#define LZMA_LC 2 +#define LZMA_LP 0 +#define LZMA_PB 0 + +/* LZMA preset choice. This is a policy decision */ +#define LZMA_PRESET ( LZMA_PRESET_DEFAULT | LZMA_PRESET_EXTREME ) + struct input_file { void *buf; size_t len; @@ -177,13 +185,75 @@ static int process_zinfo_copy ( struct input_file *input, return 0; } +#define OPCODE_CALL 0xe8 +#define OPCODE_JMP 0xe9 + +static void bcj_filter ( void *data, size_t len ) { + struct { + uint8_t opcode; + int32_t target; + } __attribute__ (( packed )) *jump; + ssize_t limit = ( len - sizeof ( *jump ) ); + ssize_t offset; + + /* liblzma does include an x86 BCJ filter, but it's hideously + * convoluted and undocumented. This BCJ filter is + * substantially simpler and achieves the same compression (at + * the cost of requiring the decompressor to know the size of + * the decompressed data, which we already have in iPXE). + */ + for ( offset = 0 ; offset <= limit ; offset++ ) { + jump = ( data + offset ); + + /* Skip instructions that are not followed by a rel32 address */ + if ( ( jump->opcode != OPCODE_CALL ) && + ( jump->opcode != OPCODE_JMP ) ) + continue; + + /* Convert rel32 address to an absolute address. To + * avoid false positives (which damage the compression + * ratio), we should check that the jump target is + * within the range [0,limit). + * + * Some output values would then end up being mapped + * from two distinct input values, making the + * transformation irreversible. To solve this, we + * transform such values back into the part of the + * range which would otherwise correspond to no input + * values. + */ + if ( ( jump->target >= -offset ) && + ( jump->target < ( limit - offset ) ) ) { + /* Convert relative addresses in the range + * [-offset,limit-offset) to absolute + * addresses in the range [0,limit). + */ + jump->target += offset; + } else if ( ( jump->target >= ( limit - offset ) ) && + ( jump->target < limit ) ) { + /* Convert positive numbers in the range + * [limit-offset,limit) to negative numbers in + * the range [-offset,0). + */ + jump->target -= limit; + } + offset += sizeof ( jump->target ); + }; +} + static int process_zinfo_pack ( struct input_file *input, struct output_file *output, union zinfo_record *zinfo ) { struct zinfo_pack *pack = &zinfo->pack; size_t offset = pack->offset; size_t len = pack->len; - unsigned long packed_len; + size_t packed_len = 0; + size_t remaining = ( output->max_len - output->len ); + lzma_options_lzma options; + const lzma_filter filters[] = { + { .id = LZMA_FILTER_LZMA1, .options = &options }, + { .id = LZMA_VLI_UNKNOWN } + }; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -196,9 +266,15 @@ static int process_zinfo_pack ( struct input_file *input, return -1; } - if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, - ( output->buf + output->len ), - &packed_len, 0 ) != UCL_E_OK ) { + bcj_filter ( ( input->buf + offset ), len ); + + lzma_lzma_preset ( &options, LZMA_PRESET ); + options.lc = LZMA_LC; + options.lp = LZMA_LP; + options.pb = LZMA_PB; + if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), + len, ( output->buf + output->len ), + &packed_len, remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } @@ -206,7 +282,7 @@ static int process_zinfo_pack ( struct input_file *input, if ( DEBUG ) { fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), output->len, - ( size_t )( output->len + packed_len ) ); + ( output->len + packed_len ) ); } output->len += packed_len; |