diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/ipxe/src/util | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip | |
Diffstat (limited to 'roms/ipxe/src/util')
31 files changed, 7654 insertions, 0 deletions
| diff --git a/roms/ipxe/src/util/.gitignore b/roms/ipxe/src/util/.gitignore new file mode 100644 index 00000000..33bedefd --- /dev/null +++ b/roms/ipxe/src/util/.gitignore @@ -0,0 +1,10 @@ +nrv2b +zbin +hijack +prototester +elf2efi32 +elf2efi64 +efirom +efifatbin +iccfix +einfo diff --git a/roms/ipxe/src/util/Makefile b/roms/ipxe/src/util/Makefile new file mode 100644 index 00000000..4a6a7c7c --- /dev/null +++ b/roms/ipxe/src/util/Makefile @@ -0,0 +1,16 @@ +BLIB = ../bin/blib.a +CFLAGS = -Os + +all : hijack mucurses_test + +hijack : hijack.c +	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -lpcap -o $@ $< + +mucurses_test.o : mucurses_test.c +	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -o $@ -c $< + +mucurses_test : mucurses_test.o $(BLIB) +	$(CC) -o $@ $< -lc $(BLIB) + +clean : +	rm -f hijack mucurses_test *.o diff --git a/roms/ipxe/src/util/Option/ROM.pm b/roms/ipxe/src/util/Option/ROM.pm new file mode 100644 index 00000000..6c396730 --- /dev/null +++ b/roms/ipxe/src/util/Option/ROM.pm @@ -0,0 +1,727 @@ +package Option::ROM; + +# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +=head1 NAME + +Option::ROM - Option ROM manipulation + +=head1 SYNOPSIS + +    use Option::ROM; + +    # Load a ROM image +    my $rom = new Option::ROM; +    $rom->load ( "rtl8139.rom" ); + +    # Modify the PCI device ID +    $rom->pci_header->{device_id} = 0x1234; +    $rom->fix_checksum(); + +    # Write ROM image out to a new file +    $rom->save ( "rtl8139-modified.rom" ); + +=head1 DESCRIPTION + +C<Option::ROM> provides a mechanism for manipulating Option ROM +images. + +=head1 METHODS + +=cut + +############################################################################## +# +# Option::ROM::Fields +# +############################################################################## + +package Option::ROM::Fields; + +use strict; +use warnings; +use Carp; +use bytes; + +sub TIEHASH { +  my $class = shift; +  my $self = shift; + +  bless $self, $class; +  return $self; +} + +sub FETCH { +  my $self = shift; +  my $key = shift; + +  return undef unless $self->EXISTS ( $key ); +  my $raw = substr ( ${$self->{data}}, +		     ( $self->{offset} + $self->{fields}->{$key}->{offset} ), +		     $self->{fields}->{$key}->{length} ); +  my $unpack = ( ref $self->{fields}->{$key}->{unpack} ? +		 $self->{fields}->{$key}->{unpack} : +		 sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } ); +  return &$unpack ( $raw ); +} + +sub STORE { +  my $self = shift; +  my $key = shift; +  my $value = shift; + +  croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key ); +  my $pack = ( ref $self->{fields}->{$key}->{pack} ? +	       $self->{fields}->{$key}->{pack} : +	       sub { pack ( $self->{fields}->{$key}->{pack}, shift ); } ); +  my $raw = &$pack ( $value ); +  substr ( ${$self->{data}}, +	   ( $self->{offset} + $self->{fields}->{$key}->{offset} ), +	   $self->{fields}->{$key}->{length} ) = $raw; +} + +sub DELETE { +  my $self = shift; +  my $key = shift; + +  $self->STORE ( $key, 0 ); +} + +sub CLEAR { +  my $self = shift; + +  foreach my $key ( keys %{$self->{fields}} ) { +    $self->DELETE ( $key ); +  } +} + +sub EXISTS { +  my $self = shift; +  my $key = shift; + +  return ( exists $self->{fields}->{$key} && +	   ( ( $self->{fields}->{$key}->{offset} + +	       $self->{fields}->{$key}->{length} ) <= $self->{length} ) ); +} + +sub FIRSTKEY { +  my $self = shift; + +  keys %{$self->{fields}}; +  return each %{$self->{fields}}; +} + +sub NEXTKEY { +  my $self = shift; +  my $lastkey = shift; + +  return each %{$self->{fields}}; +} + +sub SCALAR { +  my $self = shift; + +  return 1; +} + +sub UNTIE { +  my $self = shift; +} + +sub DESTROY { +  my $self = shift; +} + +sub checksum { +  my $self = shift; + +  my $raw = substr ( ${$self->{data}}, $self->{offset}, $self->{length} ); +  return unpack ( "%8C*", $raw ); +} + +############################################################################## +# +# Option::ROM +# +############################################################################## + +package Option::ROM; + +use strict; +use warnings; +use Carp; +use bytes; +use Exporter 'import'; + +use constant ROM_SIGNATURE => 0xaa55; +use constant PCI_SIGNATURE => 'PCIR'; +use constant PCI_LAST_IMAGE => 0x80; +use constant PNP_SIGNATURE => '$PnP'; +use constant IPXE_SIGNATURE => 'iPXE'; + +our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE +		      PNP_SIGNATURE IPXE_SIGNATURE ); +our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] ); + +use constant JMP_SHORT => 0xeb; +use constant JMP_NEAR => 0xe9; +use constant CALL_NEAR => 0xe8; + +sub pack_init { +  my $dest = shift; + +  # Always create a near jump; it's simpler +  if ( $dest ) { +    return pack ( "CS", JMP_NEAR, ( $dest - 6 ) ); +  } else { +    return pack ( "CS", 0, 0 ); +  } +} + +sub unpack_init { +  my $instr = shift; + +  # Accept both short and near jumps +  my $jump = unpack ( "C", $instr ); +  if ( $jump == JMP_SHORT ) { +    my $offset = unpack ( "xC", $instr ); +    return ( $offset + 5 ); +  } elsif ( $jump == JMP_NEAR ) { +    my $offset = unpack ( "xS", $instr ); +    return ( $offset + 6 ); +  } elsif ( $jump == CALL_NEAR ) { +    my $offset = unpack ( "xS", $instr ); +    return ( $offset + 6 ); +  } elsif ( $jump == 0 ) { +    return 0; +  } else { +    croak "Unrecognised jump instruction in init vector\n"; +  } +} + +=pod + +=item C<< new () >> + +Construct a new C<Option::ROM> object. + +=cut + +sub new { +  my $class = shift; + +  my $hash = {}; +  tie %$hash, "Option::ROM::Fields", { +    data => undef, +    offset => 0x00, +    length => 0x20, +    fields => { +      signature =>	{ offset => 0x00, length => 0x02, pack => "S" }, +      length =>		{ offset => 0x02, length => 0x01, pack => "C" }, +      # "init" is part of a jump instruction +      init =>		{ offset => 0x03, length => 0x03, +			  pack => \&pack_init, unpack => \&unpack_init }, +      checksum =>	{ offset => 0x06, length => 0x01, pack => "C" }, +      ipxe_header =>	{ offset => 0x10, length => 0x02, pack => "S" }, +      bofm_header =>	{ offset => 0x14, length => 0x02, pack => "S" }, +      undi_header =>	{ offset => 0x16, length => 0x02, pack => "S" }, +      pci_header =>	{ offset => 0x18, length => 0x02, pack => "S" }, +      pnp_header =>	{ offset => 0x1a, length => 0x02, pack => "S" }, +    }, +  }; +  bless $hash, $class; +  return $hash; +} + +=pod + +=item C<< set ( $data ) >> + +Set option ROM contents. + +=cut + +sub set { +  my $hash = shift; +  my $self = tied(%$hash); +  my $data = shift; + +  # Store data +  $self->{data} = \$data; + +  # Split out any data belonging to the next image +  delete $self->{next_image}; +  my $pci_header = $hash->pci_header(); +  if ( ( defined $pci_header ) && +       ( ! ( $pci_header->{last_image} & PCI_LAST_IMAGE ) ) ) { +    my $length = ( $pci_header->{image_length} * 512 ); +    my $remainder = substr ( $data, $length ); +    $data = substr ( $data, 0, $length ); +    $self->{next_image} = new Option::ROM; +    $self->{next_image}->set ( $remainder ); +  } +} + +=pod + +=item C<< get () >> + +Get option ROM contents. + +=cut + +sub get { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $data = ${$self->{data}}; +  $data .= $self->{next_image}->get() if $self->{next_image}; +  return $data; +} + +=pod + +=item C<< load ( $filename ) >> + +Load option ROM contents from the file C<$filename>. + +=cut + +sub load { +  my $hash = shift; +  my $self = tied(%$hash); +  my $filename = shift; + +  $self->{filename} = $filename; + +  open my $fh, "<$filename" +      or croak "Cannot open $filename for reading: $!"; +  read $fh, my $data, -s $fh; +  $hash->set ( $data ); +  close $fh; +} + +=pod + +=item C<< save ( [ $filename ] ) >> + +Write the ROM data back out to the file C<$filename>.  If C<$filename> +is omitted, the file used in the call to C<load()> will be used. + +=cut + +sub save { +  my $hash = shift; +  my $self = tied(%$hash); +  my $filename = shift; + +  $filename ||= $self->{filename}; + +  open my $fh, ">$filename" +      or croak "Cannot open $filename for writing: $!"; +  my $data = $hash->get(); +  print $fh $data; +  close $fh; +} + +=pod + +=item C<< length () >> + +Length of option ROM data.  This is the length of the file, not the +length from the ROM header length field. + +=cut + +sub length { +  my $hash = shift; +  my $self = tied(%$hash); + +  return length ${$self->{data}}; +} + +=pod + +=item C<< pci_header () >> + +Return a C<Option::ROM::PCI> object representing the ROM's PCI header, +if present. + +=cut + +sub pci_header { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $offset = $hash->{pci_header}; +  return undef unless $offset != 0; + +  return Option::ROM::PCI->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< pnp_header () >> + +Return a C<Option::ROM::PnP> object representing the ROM's PnP header, +if present. + +=cut + +sub pnp_header { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $offset = $hash->{pnp_header}; +  return undef unless $offset != 0; + +  return Option::ROM::PnP->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< undi_header () >> + +Return a C<Option::ROM::UNDI> object representing the ROM's UNDI header, +if present. + +=cut + +sub undi_header { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $offset = $hash->{undi_header}; +  return undef unless $offset != 0; + +  return Option::ROM::UNDI->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< ipxe_header () >> + +Return a C<Option::ROM::iPXE> object representing the ROM's iPXE +header, if present. + +=cut + +sub ipxe_header { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $offset = $hash->{ipxe_header}; +  return undef unless $offset != 0; + +  return Option::ROM::iPXE->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< next_image () >> + +Return a C<Option::ROM> object representing the next image within the +ROM, if present. + +=cut + +sub next_image { +  my $hash = shift; +  my $self = tied(%$hash); + +  return $self->{next_image}; +} + +=pod + +=item C<< checksum () >> + +Calculate the byte checksum of the ROM. + +=cut + +sub checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $raw = substr ( ${$self->{data}}, 0, ( $hash->{length} * 512 ) ); +  return unpack ( "%8C*", $raw ); +} + +=pod + +=item C<< fix_checksum () >> + +Fix the byte checksum of the ROM. + +=cut + +sub fix_checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +############################################################################## +# +# Option::ROM::PCI +# +############################################################################## + +package Option::ROM::PCI; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { +  my $class = shift; +  my $data = shift; +  my $offset = shift; + +  my $hash = {}; +  tie %$hash, "Option::ROM::Fields", { +    data => $data, +    offset => $offset, +    length => 0x0c, +    fields => { +      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" }, +      vendor_id =>	{ offset => 0x04, length => 0x02, pack => "S" }, +      device_id =>	{ offset => 0x06, length => 0x02, pack => "S" }, +      device_list =>	{ offset => 0x08, length => 0x02, pack => "S" }, +      struct_length =>	{ offset => 0x0a, length => 0x02, pack => "S" }, +      struct_revision =>{ offset => 0x0c, length => 0x01, pack => "C" }, +      base_class => 	{ offset => 0x0d, length => 0x01, pack => "C" }, +      sub_class => 	{ offset => 0x0e, length => 0x01, pack => "C" }, +      prog_intf => 	{ offset => 0x0f, length => 0x01, pack => "C" }, +      image_length =>	{ offset => 0x10, length => 0x02, pack => "S" }, +      revision =>	{ offset => 0x12, length => 0x02, pack => "S" }, +      code_type => 	{ offset => 0x14, length => 0x01, pack => "C" }, +      last_image => 	{ offset => 0x15, length => 0x01, pack => "C" }, +      runtime_length =>	{ offset => 0x16, length => 0x02, pack => "S" }, +      conf_header =>	{ offset => 0x18, length => 0x02, pack => "S" }, +      clp_entry =>	{ offset => 0x1a, length => 0x02, pack => "S" }, +    }, +  }; +  bless $hash, $class; + +  # Retrieve true length of structure +  my $self = tied ( %$hash ); +  $self->{length} = $hash->{struct_length}; + +  return $hash;   +} + +############################################################################## +# +# Option::ROM::PnP +# +############################################################################## + +package Option::ROM::PnP; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { +  my $class = shift; +  my $data = shift; +  my $offset = shift; + +  my $hash = {}; +  tie %$hash, "Option::ROM::Fields", { +    data => $data, +    offset => $offset, +    length => 0x06, +    fields => { +      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" }, +      struct_revision =>{ offset => 0x04, length => 0x01, pack => "C" }, +      struct_length =>	{ offset => 0x05, length => 0x01, pack => "C" }, +      checksum =>	{ offset => 0x09, length => 0x01, pack => "C" }, +      manufacturer =>	{ offset => 0x0e, length => 0x02, pack => "S" }, +      product =>	{ offset => 0x10, length => 0x02, pack => "S" }, +      bcv =>		{ offset => 0x16, length => 0x02, pack => "S" }, +      bdv =>		{ offset => 0x18, length => 0x02, pack => "S" }, +      bev =>		{ offset => 0x1a, length => 0x02, pack => "S" }, +    }, +  }; +  bless $hash, $class; + +  # Retrieve true length of structure +  my $self = tied ( %$hash ); +  $self->{length} = ( $hash->{struct_length} * 16 ); + +  return $hash;   +} + +sub checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  return $self->checksum(); +} + +sub fix_checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +sub manufacturer { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $manufacturer = $hash->{manufacturer}; +  return undef unless $manufacturer; + +  my $raw = substr ( ${$self->{data}}, $manufacturer ); +  return unpack ( "Z*", $raw ); +} + +sub product { +  my $hash = shift; +  my $self = tied(%$hash); + +  my $product = $hash->{product}; +  return undef unless $product; + +  my $raw = substr ( ${$self->{data}}, $product ); +  return unpack ( "Z*", $raw ); +} + +############################################################################## +# +# Option::ROM::UNDI +# +############################################################################## + +package Option::ROM::UNDI; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { +  my $class = shift; +  my $data = shift; +  my $offset = shift; + +  my $hash = {}; +  tie %$hash, "Option::ROM::Fields", { +    data => $data, +    offset => $offset, +    length => 0x16, +    fields => { +      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" }, +      struct_length =>	{ offset => 0x04, length => 0x01, pack => "C" }, +      checksum =>	{ offset => 0x05, length => 0x01, pack => "C" }, +      struct_revision =>{ offset => 0x06, length => 0x01, pack => "C" }, +      version_revision =>{ offset => 0x07, length => 0x01, pack => "C" }, +      version_minor =>	{ offset => 0x08, length => 0x01, pack => "C" }, +      version_major =>	{ offset => 0x09, length => 0x01, pack => "C" }, +      loader_entry =>	{ offset => 0x0a, length => 0x02, pack => "S" }, +      stack_size =>	{ offset => 0x0c, length => 0x02, pack => "S" }, +      data_size =>	{ offset => 0x0e, length => 0x02, pack => "S" }, +      code_size =>	{ offset => 0x10, length => 0x02, pack => "S" }, +      bus_type =>	{ offset => 0x12, length => 0x04, pack => "a4" }, +    }, +  }; +  bless $hash, $class; + +  # Retrieve true length of structure +  my $self = tied ( %$hash ); +  $self->{length} = $hash->{struct_length}; + +  return $hash; +} + +sub checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  return $self->checksum(); +} + +sub fix_checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +############################################################################## +# +# Option::ROM::iPXE +# +############################################################################## + +package Option::ROM::iPXE; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { +  my $class = shift; +  my $data = shift; +  my $offset = shift; + +  my $hash = {}; +  tie %$hash, "Option::ROM::Fields", { +    data => $data, +    offset => $offset, +    length => 0x06, +    fields => { +      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" }, +      struct_length =>	{ offset => 0x04, length => 0x01, pack => "C" }, +      checksum =>	{ offset => 0x05, length => 0x01, pack => "C" }, +      shrunk_length =>	{ offset => 0x06, length => 0x01, pack => "C" }, +      build_id =>	{ offset => 0x08, length => 0x04, pack => "L" }, +    }, +  }; +  bless $hash, $class; + +  # Retrieve true length of structure +  my $self = tied ( %$hash ); +  $self->{length} = $hash->{struct_length}; + +  return $hash; +} + +sub checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  return $self->checksum(); +} + +sub fix_checksum { +  my $hash = shift; +  my $self = tied(%$hash); + +  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +1; diff --git a/roms/ipxe/src/util/catrom.pl b/roms/ipxe/src/util/catrom.pl new file mode 100755 index 00000000..da99d7b9 --- /dev/null +++ b/roms/ipxe/src/util/catrom.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +use warnings; +use strict; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +my @romfiles = @ARGV +    or die "Usage: $0 rom-file-1 rom-file-2 ... > multi-rom-file\n"; + +while ( my $romfile = shift @romfiles ) { + +  # Read ROM file +  my $rom = new Option::ROM; +  $rom->load ( $romfile ); + +  # Tag final image as non-final in all except the final ROM +  if ( @romfiles ) { +    my $image = $rom; +    $image = $image->next_image() while $image->next_image(); +    $image->pci_header->{last_image} &= ~PCI_LAST_IMAGE; +    $image->fix_checksum(); +  } + +  # Write ROM file to STDOUT +  $rom->save ( "-" ); +} diff --git a/roms/ipxe/src/util/diffsize.pl b/roms/ipxe/src/util/diffsize.pl new file mode 100755 index 00000000..ced07862 --- /dev/null +++ b/roms/ipxe/src/util/diffsize.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl -w +# usage: +# [somebody@somewhere ~/ipxe/src]$ ./util/diffsize.pl [<old rev> [<new rev>]] +# by default <old rev> is HEAD and <new rev> is the working tree + +use strict; + +-d "bin" or die "Please run me in the iPXE src directory\n"; +mkdir ".sizes"; + +my($oldrev, $newrev); +my($oldname, $newname); + +if (@ARGV) { +  $oldname = shift; +} else { +  $oldname = "HEAD"; +} + +if (@ARGV) { +  $newname = shift; +} else { +  $newrev = "tree" . time(); +} + +$oldrev = `git rev-parse $oldname`; +chomp $oldrev; + +unless (defined $newrev) { +  $newrev = `git rev-parse $newname`; +  chomp $newrev; +} + +sub calc_sizes($$) { +  my($name, $rev) = @_; +  my $output; +  my $lastrev; +  my $stashed = 0; +  my $res = 0; + +  return if -e ".sizes/$rev.sizes"; + +  if (defined $name) { +    $output = `git stash`; +    $stashed = 1 unless $output =~ /No local changes to save/; +    $lastrev = `git name-rev --name-only HEAD`; +    system("git checkout $name >/dev/null"); $res ||= $?; +  } + +  system("make -j4 bin/ipxe.lkrn >/dev/null"); $res ||= $?; +  system("make bin/ipxe.lkrn.sizes > .sizes/$rev.sizes"); $res ||= $?; + +  if (defined $name) { +    system("git checkout $lastrev >/dev/null"); $res ||= $?; +    system("git stash pop >/dev/null") if $stashed; $res ||= $?; +  } + +  if ($res) { +    unlink(".sizes/$rev.sizes"); +    die "Error making sizes file\n"; +  } +} + +our %Sizes; + +sub save_sizes($$) { +  my($id, $rev) = @_; +  my $file = ".sizes/$rev.sizes"; + +  open SIZES, $file or die "opening $file: $!\n"; +  while (<SIZES>) { +    my($text, $data, $bss, $total, $hex, $name) = split; +    $name =~ s|bin/||; $name =~ s|\.o$||; + +    # Skip the header and totals lines +    next if $total =~ /[a-z]/ or $name =~ /TOTALS/; + +    # Skip files named with dash, due to old Makefile bug +    next if $name =~ /-/; + +    $Sizes{$name} = {old => 0, new => 0} unless exists $Sizes{$name}; +    $Sizes{$name}{$id} = $total; +  } +} + +calc_sizes($oldname, $oldrev); +calc_sizes($newname, $newrev); + +save_sizes('old', $oldrev); +save_sizes('new', $newrev); + +my $total = 0; + +for (sort keys %Sizes) { +  my $diff = $Sizes{$_}{new} - $Sizes{$_}{old}; +  if (abs($diff) >= 16) { +    printf "%12s %+d\n", substr($_, 0, 12), $Sizes{$_}{new} - $Sizes{$_}{old}; +  } +  $total += $diff; +} +printf "      TOTAL: %+d\n", $total; diff --git a/roms/ipxe/src/util/disrom.pl b/roms/ipxe/src/util/disrom.pl new file mode 100755 index 00000000..574957ac --- /dev/null +++ b/roms/ipxe/src/util/disrom.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +my $romfile = shift || "-"; +my $rom = new Option::ROM; +$rom->load ( $romfile ); + +do { + +  die "Not an option ROM image\n" +      unless $rom->{signature} == ROM_SIGNATURE; + +  my $romlength = ( $rom->{length} * 512 ); +  my $filelength = $rom->length; +  die "ROM image truncated (is $filelength, should be $romlength)\n" +      if $filelength < $romlength; + +  printf "ROM header:\n\n"; +  printf "  %-16s 0x%02x (%d)\n", "Length:", +	 $rom->{length}, ( $rom->{length} * 512 ); +  printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $rom->{checksum}, +	 ( ( $rom->checksum == 0 ) ? "" : "INCORRECT: " ), $rom->checksum; +  printf "  %-16s 0x%04x\n", "Init:", $rom->{init}; +  printf "  %-16s 0x%04x\n", "UNDI header:", $rom->{undi_header}; +  printf "  %-16s 0x%04x\n", "PCI header:", $rom->{pci_header}; +  printf "  %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header}; +  printf "\n"; + +  my $pci = $rom->pci_header(); +  if ( $pci ) { +    printf "PCI header:\n\n"; +    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}; +    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:", +	   $pci->{image_length}, ( $pci->{image_length} * 512 ); +    printf "  %-16s 0x%04x (%d)\n", "Runtime length:", +	   $pci->{runtime_length}, ( $pci->{runtime_length} * 512 ); +    printf "  %-16s 0x%02x\n", "Code type:", $pci->{code_type}; +    if ( exists $pci->{conf_header} ) { +      printf "  %-16s 0x%04x\n", "Config header:", $pci->{conf_header}; +      printf "  %-16s 0x%04x\n", "CLP entry:", $pci->{clp_entry}; +    } +    printf "\n"; +  } + +  my $pnp = $rom->pnp_header(); +  if ( $pnp ) { +    printf "PnP header:\n\n"; +    printf "  %-16s %s\n", "Signature:", $pnp->{signature}; +    printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $pnp->{checksum}, +	   ( ( $pnp->checksum == 0 ) ? "" : "INCORRECT: " ), $pnp->checksum; +    printf "  %-16s 0x%04x \"%s\"\n", "Manufacturer:", +	   $pnp->{manufacturer}, $pnp->manufacturer; +    printf "  %-16s 0x%04x \"%s\"\n", "Product:", +	   $pnp->{product}, $pnp->product; +    printf "  %-16s 0x%04x\n", "BCV:", $pnp->{bcv}; +    printf "  %-16s 0x%04x\n", "BDV:", $pnp->{bdv}; +    printf "  %-16s 0x%04x\n", "BEV:", $pnp->{bev}; +    printf "\n"; +  } + +  my $undi = $rom->undi_header(); +  if ( $undi ) { +    printf "UNDI header:\n\n"; +    printf "  %-16s %s\n", "Signature:", $undi->{signature}; +    printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $undi->{checksum}, +	   ( ( $undi->checksum == 0 ) ? "" : "INCORRECT: " ), $undi->checksum; +    printf "  %-16s %d.%d.%d\n", "UNDI version:", $undi->{version_major}, +	   $undi->{version_minor}, $undi->{version_revision}; +    printf "  %-16s 0x%04x\n", "Loader entry:", $undi->{loader_entry}; +    printf "  %-16s 0x%04x\n", "Stack size:", $undi->{stack_size}; +    printf "  %-16s 0x%04x\n", "Data size:", $undi->{data_size}; +    printf "  %-16s 0x%04x\n", "Code size:", $undi->{code_size}; +    printf "  %-16s %s\n", "Bus type:", $undi->{bus_type}; +    printf "\n"; +  } + +  my $ipxe = $rom->ipxe_header(); +  if ( $ipxe ) { +    printf "iPXE header:\n\n"; +    printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $ipxe->{checksum}, +	   ( ( $ipxe->checksum == 0 ) ? "" : "INCORRECT: " ), $ipxe->checksum; +    printf "  %-16s 0x%02x (%d)\n", "Shrunk length:", +	   $ipxe->{shrunk_length}, ( $ipxe->{shrunk_length} * 512 ); +    printf "  %-16s 0x%08x\n", "Build ID:", $ipxe->{build_id}; +    printf "\n"; +  } + +} while ( $rom = $rom->next_image ); diff --git a/roms/ipxe/src/util/efifatbin.c b/roms/ipxe/src/util/efifatbin.c new file mode 100644 index 00000000..c02f750b --- /dev/null +++ b/roms/ipxe/src/util/efifatbin.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/IndustryStandard/PeImage.h> + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { +}; + +/** EFI fat binary file header */ +struct efifatbin_file_header { +	/** Signature */ +	uint32_t signature; +	/** Count */ +	uint32_t count; +} __attribute__ (( packed )); + +/** EFI fat binary signature */ +#define EFIFATBIN_SIGNATURE 0x0ef1fab9 + +/** EFI fat binary image header */ +struct efifatbin_image_header { +	/** Flags */ +	uint64_t flags; +	/** Offset */ +	uint32_t offset; +	/** Length */ +	uint32_t len; +	/** Padding */ +	uint32_t pad; +} __attribute__ (( packed )); + +/** EFI fat binary default flags */ +#define EFIFATBIN_FLAGS 0x0000000300000007ULL + +/** EFI fat binary 64-bit flag */ +#define EFIFATBIN_64BIT 0x0000000001000000ULL + +/** + * Allocate memory + * + * @v len		Length of memory to allocate + * @ret ptr		Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { +	void *ptr; + +	ptr = malloc ( len ); +	if ( ! ptr ) { +		eprintf ( "Could not allocate %zd bytes\n", len ); +		exit ( 1 ); +	} + +	return ptr; +} + +/** + * Generate EFI fat binary + * + * @v count		Number of input files + * @v infile_names	Input filenames + * @v outfile_name	Output filename + */ +static void make_efifatbin ( unsigned int count, char **infile_names, +			     const char *outfile_name ) { +	FILE *infile[count]; +	FILE *outfile; +	struct stat stat[count]; +	void *buf[count]; +	struct efifatbin_file_header file_header; +	struct efifatbin_image_header header[count]; +	size_t offset; +	EFI_IMAGE_DOS_HEADER *dos; +	union { +		EFI_IMAGE_NT_HEADERS32 nt32; +		EFI_IMAGE_NT_HEADERS64 nt64; +	} *nt; +	unsigned int i; + +	/* Generate file header */ +	file_header.signature = EFIFATBIN_SIGNATURE; +	file_header.count = count; +	offset = ( sizeof ( file_header ) + sizeof ( header ) ); + +	/* Process input files */ +	for ( i = 0 ; i < count ; i++ ) { + +		/* Open input file */ +		infile[i] = fopen ( infile_names[i], "r" ); +		if ( ! infile[i] ) { +			eprintf ( "Could not open %s for reading: %s\n", +				  infile_names[i], strerror ( errno ) ); +			exit ( 1 ); +		} + +		/* Determine PE file size */ +		if ( fstat ( fileno ( infile[i] ), &stat[i] ) != 0 ) { +			eprintf ( "Could not stat %s: %s\n", +				  infile_names[i], strerror ( errno ) ); +			exit ( 1 ); +		} + +		/* Allocate buffer and read in PE file */ +		buf[i] = xmalloc ( stat[i].st_size ); +		if ( fread ( buf[i], stat[i].st_size, 1, infile[i] ) != 1 ) { +			eprintf ( "Could not read %s: %s\n", +				  infile_names[i], strerror ( errno ) ); +			exit ( 1 ); +		} + +		/* Close input file */ +		fclose ( infile[i] ); + +		/* Generate image header */ +		header[i].flags = EFIFATBIN_FLAGS; +		header[i].offset = offset; +		header[i].len = stat[i].st_size; +		header[i].pad = 0; + +		/* Determine architecture */ +		dos = buf[i]; +		nt = ( buf[i] + dos->e_lfanew ); +		if ( nt->nt32.FileHeader.Machine == EFI_IMAGE_MACHINE_X64 ) +			header[i].flags |= EFIFATBIN_64BIT; + +		/* Allow space for this image */ +		offset += stat[i].st_size; +	} + +	/* Open output file */ +	outfile = fopen ( outfile_name, "w" ); +	if ( ! outfile ) { +		eprintf ( "Could not open %s for writing: %s\n", +			  outfile_name, strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* Write fat binary header */ +	if ( fwrite ( &file_header, sizeof ( file_header ), 1, outfile ) != 1 ){ +		eprintf ( "Could not write %s: %s\n", +			  outfile_name, strerror ( errno ) ); +		exit ( 1 ); +	} +	for ( i = 0 ; i < count ; i++ ) { +		if ( fwrite ( &header[i], sizeof ( header[i] ), 1, +			      outfile ) != 1 ) { +			eprintf ( "Could not write %s: %s\n", +				  outfile_name, strerror ( errno ) ); +			exit ( 1 ); +		} +	} + +	/* Write images */ +	for ( i = 0 ; i < count ; i++ ) { +		if ( fwrite ( buf[i], stat[i].st_size, 1, outfile ) != 1 ) { +			eprintf ( "Could not write %s: %s\n", +				  outfile_name, strerror ( errno ) ); +			exit ( 1 ); +		} +	} + +	/* Close output file */ +	fclose ( outfile ); +} + +/** + * Print help + * + * @v program_name	Program name + */ +static void print_help ( const char *program_name ) { +	eprintf ( "Syntax: %s infile [infile...] outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc		Argument count + * @v argv		Argument list + * @v opts		Options structure to populate + */ +static int parse_options ( const int argc, char **argv, +			   struct options *opts __attribute__ (( unused )) ) { +	int c; + +	while (1) { +		int option_index = 0; +		static struct option long_options[] = { +			{ "help", 0, NULL, 'h' }, +			{ 0, 0, 0, 0 } +		}; + +		if ( ( c = getopt_long ( argc, argv, "h", +					 long_options, +					 &option_index ) ) == -1 ) { +			break; +		} + +		switch ( c ) { +		case 'h': +			print_help ( argv[0] ); +			exit ( 0 ); +		case '?': +		default: +			exit ( 2 ); +		} +	} +	return optind; +} + +int main ( int argc, char **argv ) { +	struct options opts; +	int infile_index; +	int outfile_index; +	int count; + +	/* Parse command-line arguments */ +	memset ( &opts, 0, sizeof ( opts ) ); +	infile_index = parse_options ( argc, argv, &opts ); +	outfile_index = ( argc - 1 ); +	count = ( outfile_index - infile_index ); +	if ( count <= 0 ) { +		print_help ( argv[0] ); +		exit ( 2 ); +	} + +	/* Generate fat binary */ +	make_efifatbin ( count, &argv[infile_index], argv[outfile_index] ); + +	return 0; +} diff --git a/roms/ipxe/src/util/efirom.c b/roms/ipxe/src/util/efirom.c new file mode 100644 index 00000000..abee496d --- /dev/null +++ b/roms/ipxe/src/util/efirom.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/IndustryStandard/PeImage.h> +#include <ipxe/efi/IndustryStandard/Pci22.h> + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { +	uint16_t vendor; +	uint16_t device; +}; + +/** + * Allocate memory + * + * @v len		Length of memory to allocate + * @ret ptr		Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { +	void *ptr; + +	ptr = malloc ( len ); +	if ( ! ptr ) { +		eprintf ( "Could not allocate %zd bytes\n", len ); +		exit ( 1 ); +	} + +	return ptr; +} + +/** + * Read information from PE headers + * + * @v pe		PE file + * @ret machine		Machine type + * @ret subsystem	EFI subsystem + */ +static void read_pe_info ( void *pe, uint16_t *machine, +			   uint16_t *subsystem ) { +	EFI_IMAGE_DOS_HEADER *dos; +	union { +		EFI_IMAGE_NT_HEADERS32 nt32; +		EFI_IMAGE_NT_HEADERS64 nt64; +	} *nt; + +	/* Locate NT header */ +	dos = pe; +	nt = ( pe + dos->e_lfanew ); + +	/* Parse out PE information */ +	*machine = nt->nt32.FileHeader.Machine; +	switch ( *machine ) { +	case EFI_IMAGE_MACHINE_IA32: +		*subsystem = nt->nt32.OptionalHeader.Subsystem; +		break; +	case EFI_IMAGE_MACHINE_X64: +		*subsystem = nt->nt64.OptionalHeader.Subsystem; +		break; +	default: +		eprintf ( "Unrecognised machine type %04x\n", *machine ); +		exit ( 1 ); +	} +} + +/** + * Convert EFI image to ROM image + * + * @v pe		EFI file + * @v rom		ROM file + */ +static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) { +	struct { +		EFI_PCI_EXPANSION_ROM_HEADER rom; +		PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) )); +		uint8_t checksum; +	} *headers; +	struct stat pe_stat; +	size_t pe_size; +	size_t rom_size; +	void *buf; +	void *payload; +	unsigned int i; +	uint8_t checksum; + +	/* Determine PE file size */ +	if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) { +		eprintf ( "Could not stat PE file: %s\n", +			  strerror ( errno ) ); +		exit ( 1 ); +	} +	pe_size = pe_stat.st_size; + +	/* Determine ROM file size */ +	rom_size = ( ( pe_size + sizeof ( *headers ) + 511 ) & ~511 ); + +	/* Allocate ROM buffer and read in PE file */ +	buf = xmalloc ( rom_size ); +	memset ( buf, 0, rom_size ); +	headers = buf; +	payload = ( buf + sizeof ( *headers ) ); +	if ( fread ( payload, pe_size, 1, pe ) != 1 ) { +		eprintf ( "Could not read PE file: %s\n", +			  strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* 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.EfiImageHeaderOffset = sizeof ( *headers ); +	headers->rom.PcirOffset = +		offsetof ( typeof ( *headers ), pci ); +	headers->pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE; +	headers->pci.VendorId = opts->vendor; +	headers->pci.DeviceId = opts->device; +	headers->pci.Length = sizeof ( headers->pci ); +	headers->pci.ClassCode[0] = PCI_CLASS_NETWORK; +	headers->pci.ImageLength = ( rom_size / 512 ); +	headers->pci.CodeType = 0x03; /* No constant in EFI headers? */ +	headers->pci.Indicator = 0x80; /* No constant in EFI headers? */ + +	/* Fix image checksum */ +	for ( i = 0, checksum = 0 ; i < rom_size ; i++ ) +		checksum += *( ( uint8_t * ) buf + i ); +	headers->checksum -= checksum; + +	/* Write out ROM */ +	if ( fwrite ( buf, rom_size, 1, rom ) != 1 ) { +		eprintf ( "Could not write ROM file: %s\n", +			  strerror ( errno ) ); +		exit ( 1 ); +	} +} + +/** + * Print help + * + * @v program_name	Program name + */ +static void print_help ( const char *program_name ) { +	eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] " +		  "infile outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc		Argument count + * @v argv		Argument list + * @v opts		Options structure to populate + */ +static int parse_options ( const int argc, char **argv, +			   struct options *opts ) { +	char *end; +	int c; + +	while (1) { +		int option_index = 0; +		static struct option long_options[] = { +			{ "vendor", required_argument, NULL, 'v' }, +			{ "device", required_argument, NULL, 'd' }, +			{ "help", 0, NULL, 'h' }, +			{ 0, 0, 0, 0 } +		}; + +		if ( ( c = getopt_long ( argc, argv, "v:d:h", +					 long_options, +					 &option_index ) ) == -1 ) { +			break; +		} + +		switch ( c ) { +		case 'v': +			opts->vendor = strtoul ( optarg, &end, 16 ); +			if ( *end ) { +				eprintf ( "Invalid vendor \"%s\"\n", optarg ); +				exit ( 2 ); +			} +			break; +		case 'd': +			opts->device = strtoul ( optarg, &end, 16 ); +			if ( *end ) { +				eprintf ( "Invalid device \"%s\"\n", optarg ); +				exit ( 2 ); +			} +			break; +		case 'h': +			print_help ( argv[0] ); +			exit ( 0 ); +		case '?': +		default: +			exit ( 2 ); +		} +	} +	return optind; +} + +int main ( int argc, char **argv ) { +	struct options opts; +	int infile_index; +	const char *infile_name; +	const char *outfile_name; +	FILE *infile; +	FILE *outfile; + +	/* Parse command-line arguments */ +	memset ( &opts, 0, sizeof ( opts ) ); +	infile_index = parse_options ( argc, argv, &opts ); +	if ( argc != ( infile_index + 2 ) ) { +		print_help ( argv[0] ); +		exit ( 2 ); +	} +	infile_name = argv[infile_index]; +	outfile_name = argv[infile_index + 1]; + +	/* Open input and output files */ +	infile = fopen ( infile_name, "r" ); +	if ( ! infile ) { +		eprintf ( "Could not open %s for reading: %s\n", +			  infile_name, strerror ( errno ) ); +		exit ( 1 ); +	} +	outfile = fopen ( outfile_name, "w" ); +	if ( ! outfile ) { +		eprintf ( "Could not open %s for writing: %s\n", +			  outfile_name, strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* Convert file */ +	make_efi_rom ( infile, outfile, &opts ); + +	fclose ( outfile ); +	fclose ( infile ); + +	return 0; +} diff --git a/roms/ipxe/src/util/einfo.c b/roms/ipxe/src/util/einfo.c new file mode 100644 index 00000000..d2155898 --- /dev/null +++ b/roms/ipxe/src/util/einfo.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { +}; + +/** Error usage information */ +struct einfo { +	/** Size of error information record */ +	uint32_t size; +	/** Error number */ +	uint32_t error; +	/** Offset to error description (NUL-terminated) */ +	uint32_t desc; +	/** Offset to file name (NUL-terminated) */ +	uint32_t file; +	/** Line number */ +	uint32_t line; +} __attribute__ (( packed )); + +/** + * Process einfo file + * + * @v infile		Filename + * @v opts		Command-line options + */ +static void einfo ( const char *infile, +		    struct options *opts __attribute__ (( unused )) ) { +	int fd; +	struct stat stat; +	size_t len; +	void *start; +	struct einfo *einfo; + +	/* Open einfo file */ +	if ( ( fd = open ( infile, O_RDONLY ) ) < 0 ) { +		eprintf ( "Cannot open \"%s\": %s\n", +			  infile, strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* Get file size */ +	if ( fstat ( fd, &stat ) < 0 ) { +		eprintf ( "Cannot stat \"%s\": %s\n", +			  infile, strerror ( errno ) ); +		exit ( 1 ); +	} +	len = stat.st_size; + +	if ( len ) { + +		/* Map file */ +		if ( ( start = mmap ( NULL, len, PROT_READ, MAP_SHARED, +				      fd, 0 ) ) == MAP_FAILED ) { +			eprintf ( "Cannot mmap \"%s\": %s\n", +				  infile, strerror ( errno ) ); +			exit ( 1 ); +		} + +		/* Iterate over einfo records */ +		for ( einfo = start ; ( ( void * ) einfo ) < ( start + len ) ; +		      einfo = ( ( ( void * ) einfo ) + einfo->size ) ) { +			printf ( "%08x\t%s\t%d\t%s\n", einfo->error, +				 ( ( ( char * ) einfo ) + einfo->file ), +				 einfo->line, +				 ( ( ( char * ) einfo ) + einfo->desc ) ); +		} + +		/* Unmap file */ +		munmap ( start, len ); +	} + +	/* Close file */ +	close ( fd ); +} + +/** + * Print help + * + * @v program_name	Program name + */ +static void print_help ( const char *program_name ) { +	eprintf ( "Syntax: %s file1.einfo [file2.einfo...]\n", +		  program_name ); +} + +/** + * Parse command-line options + * + * @v argc		Argument count + * @v argv		Argument list + * @v opts		Options structure to populate + */ +static int parse_options ( const int argc, char **argv, +			   struct options *opts __attribute__ (( unused )) ) { +	int c; + +	while (1) { +		int option_index = 0; +		static struct option long_options[] = { +			{ "help", 0, NULL, 'h' }, +			{ 0, 0, 0, 0 } +		}; + +		if ( ( c = getopt_long ( argc, argv, "s:h", +					 long_options, +					 &option_index ) ) == -1 ) { +			break; +		} + +		switch ( c ) { +		case 'h': +			print_help ( argv[0] ); +			exit ( 0 ); +		case '?': +		default: +			exit ( 2 ); +		} +	} +	return optind; +} + +int main ( int argc, char **argv ) { +	struct options opts = { +	}; +	int infile_index; +	const char *infile; + +	/* Parse command-line arguments */ +	infile_index = parse_options ( argc, argv, &opts ); +	if ( argc <= infile_index ) { +		print_help ( argv[0] ); +		exit ( 2 ); +	} + +	/* Process each einfo file */ +	for ( ; infile_index < argc ; infile_index++ ) { +		infile = argv[infile_index]; +		einfo ( infile, &opts ); +	} + +	return 0; +} diff --git a/roms/ipxe/src/util/elf2efi.c b/roms/ipxe/src/util/elf2efi.c new file mode 100644 index 00000000..45d53957 --- /dev/null +++ b/roms/ipxe/src/util/elf2efi.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define _GNU_SOURCE +#define PACKAGE "elf2efi" +#define PACKAGE_VERSION "1" +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <bfd.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/IndustryStandard/PeImage.h> +#include <libgen.h> + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +#define EFI_FILE_ALIGN 0x20 + +struct pe_section { +	struct pe_section *next; +	EFI_IMAGE_SECTION_HEADER hdr; +	uint8_t contents[0]; +}; + +struct pe_relocs { +	struct pe_relocs *next; +	unsigned long start_rva; +	unsigned int used_relocs; +	unsigned int total_relocs; +	uint16_t *relocs; +}; + +struct pe_header { +	EFI_IMAGE_DOS_HEADER dos; +	uint8_t padding[128]; +#if defined(EFI_TARGET_IA32) +	EFI_IMAGE_NT_HEADERS32 nt; +#elif defined(EFI_TARGET_X64) +	EFI_IMAGE_NT_HEADERS64 nt; +#endif +}; + +static struct pe_header efi_pe_header = { +	.dos = { +		.e_magic = EFI_IMAGE_DOS_SIGNATURE, +		.e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ), +	}, +	.nt = { +		.Signature = EFI_IMAGE_NT_SIGNATURE, +		.FileHeader = { +#if defined(EFI_TARGET_IA32) +			.Machine = EFI_IMAGE_MACHINE_IA32, +#elif defined(EFI_TARGET_X64) +			.Machine = EFI_IMAGE_MACHINE_X64, +#endif +			.TimeDateStamp = 0x10d1a884, +			.SizeOfOptionalHeader = +				sizeof ( efi_pe_header.nt.OptionalHeader ), +			.Characteristics = ( EFI_IMAGE_FILE_DLL | +#if defined(EFI_TARGET_IA32) +					     EFI_IMAGE_FILE_32BIT_MACHINE | +#endif +					     EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), +		}, +		.OptionalHeader = { +#if defined(EFI_TARGET_IA32) +			.Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, +#elif defined(EFI_TARGET_X64) +			.Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, +#endif +			.SectionAlignment = EFI_FILE_ALIGN, +			.FileAlignment = EFI_FILE_ALIGN, +			.SizeOfImage = sizeof ( efi_pe_header ), +			.SizeOfHeaders = sizeof ( efi_pe_header ), +			.NumberOfRvaAndSizes = +				EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, +		}, +	}, +}; + +/** Command-line options */ +struct options { +	unsigned int subsystem; +}; + +/** + * Allocate memory + * + * @v len		Length of memory to allocate + * @ret ptr		Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { +	void *ptr; + +	ptr = malloc ( len ); +	if ( ! ptr ) { +		eprintf ( "Could not allocate %zd bytes\n", len ); +		exit ( 1 ); +	} + +	return ptr; +} + +/** + * Align section within PE file + * + * @v offset		Unaligned offset + * @ret aligned_offset	Aligned offset + */ +static unsigned long efi_file_align ( unsigned long offset ) { +	return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) ); +} + +/** + * Generate entry in PE relocation table + * + * @v pe_reltab		PE relocation table + * @v rva		RVA + * @v size		Size of relocation entry + */ +static void generate_pe_reloc ( struct pe_relocs **pe_reltab, +				unsigned long rva, size_t size ) { +	unsigned long start_rva; +	uint16_t reloc; +	struct pe_relocs *pe_rel; +	uint16_t *relocs; + +	/* Construct */ +	start_rva = ( rva & ~0xfff ); +	reloc = ( rva & 0xfff ); +	switch ( size ) { +	case 8: +		reloc |= 0xa000; +		break; +	case 4: +		reloc |= 0x3000; +		break; +	case 2: +		reloc |= 0x2000; +		break; +	default: +		eprintf ( "Unsupported relocation size %zd\n", size ); +		exit ( 1 ); +	} + +	/* Locate or create PE relocation table */ +	for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { +		if ( pe_rel->start_rva == start_rva ) +			break; +	} +	if ( ! pe_rel ) { +		pe_rel = xmalloc ( sizeof ( *pe_rel ) ); +		memset ( pe_rel, 0, sizeof ( *pe_rel ) ); +		pe_rel->next = *pe_reltab; +		*pe_reltab = pe_rel; +		pe_rel->start_rva = start_rva; +	} + +	/* Expand relocation list if necessary */ +	if ( pe_rel->used_relocs < pe_rel->total_relocs ) { +		relocs = pe_rel->relocs; +	} else { +		pe_rel->total_relocs = ( pe_rel->total_relocs ? +					 ( pe_rel->total_relocs * 2 ) : 256 ); +		relocs = xmalloc ( pe_rel->total_relocs * +				   sizeof ( pe_rel->relocs[0] ) ); +		memset ( relocs, 0, +			 pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) ); +		memcpy ( relocs, pe_rel->relocs, +			 pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) ); +		free ( pe_rel->relocs ); +		pe_rel->relocs = relocs; +	} + +	/* Store relocation */ +	pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc; +} + +/** + * Calculate size of binary PE relocation table + * + * @v pe_reltab		PE relocation table + * @v buffer		Buffer to contain binary table, or NULL + * @ret size		Size of binary table + */ +static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, +				 void *buffer ) { +	struct pe_relocs *pe_rel; +	unsigned int num_relocs; +	size_t size; +	size_t total_size = 0; + +	for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { +		num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 ); +		size = ( sizeof ( uint32_t ) /* VirtualAddress */ + +			 sizeof ( uint32_t ) /* SizeOfBlock */ + +			 ( num_relocs * sizeof ( uint16_t ) ) ); +		if ( buffer ) { +			*( (uint32_t *) ( buffer + total_size + 0 ) ) +				= pe_rel->start_rva; +			*( (uint32_t *) ( buffer + total_size + 4 ) ) = size; +			memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs, +				 ( num_relocs * sizeof ( uint16_t ) ) ); +		} +		total_size += size; +	} + +	return total_size; +} + +/** + * Open input BFD file + * + * @v filename		File name + * @ret ibfd		BFD file + */ +static bfd * open_input_bfd ( const char *filename ) { +	bfd *bfd; + +	/* Open the file */ +	bfd = bfd_openr ( filename, NULL ); +	if ( ! bfd ) { +		eprintf ( "Cannot open %s: ", filename ); +		bfd_perror ( NULL ); +		exit ( 1 ); +	} + +	/* The call to bfd_check_format() must be present, otherwise +	 * we get a segfault from later BFD calls. +	 */ +	if ( ! bfd_check_format ( bfd, bfd_object ) ) { +		eprintf ( "%s is not an object file: ", filename ); +		bfd_perror ( NULL ); +		exit ( 1 ); +	} + +	return bfd; +} + +/** + * Read symbol table + * + * @v bfd		BFD file + */ +static asymbol ** read_symtab ( bfd *bfd ) { +	long symtab_size; +	asymbol **symtab; +	long symcount; + +	/* Get symbol table size */ +	symtab_size = bfd_get_symtab_upper_bound ( bfd ); +	if ( symtab_size < 0 ) { +		bfd_perror ( "Could not get symbol table upper bound" ); +		exit ( 1 ); +	} + +	/* Allocate and read symbol table */ +	symtab = xmalloc ( symtab_size ); +	symcount = bfd_canonicalize_symtab ( bfd, symtab ); +	if ( symcount < 0 ) { +		bfd_perror ( "Cannot read symbol table" ); +		exit ( 1 ); +	} + +	return symtab; +} + +/** + * Read relocation table + * + * @v bfd		BFD file + * @v symtab		Symbol table + * @v section		Section + * @v symtab		Symbol table + * @ret reltab		Relocation table + */ +static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, +				asection *section ) { +	long reltab_size; +	arelent **reltab; +	long numrels; + +	/* Get relocation table size */ +	reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); +	if ( reltab_size < 0 ) { +		bfd_perror ( "Could not get relocation table upper bound" ); +		exit ( 1 ); +	} + +	/* Allocate and read relocation table */ +	reltab = xmalloc ( reltab_size ); +	numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); +	if ( numrels < 0 ) { +		bfd_perror ( "Cannot read relocation table" ); +		exit ( 1 ); +	} + +	return reltab; +} + +/** + * Process section + * + * @v bfd		BFD file + * @v pe_header		PE file header + * @v section		Section + * @ret new		New PE section + */ +static struct pe_section * process_section ( bfd *bfd, +					     struct pe_header *pe_header, +					     asection *section ) { +	struct pe_section *new; +	size_t section_memsz; +	size_t section_filesz; +	unsigned long flags = bfd_get_section_flags ( bfd, section ); +	unsigned long code_start; +	unsigned long code_end; +	unsigned long data_start; +	unsigned long data_mid; +	unsigned long data_end; +	unsigned long start; +	unsigned long end; +	unsigned long *applicable_start; +	unsigned long *applicable_end; + +	/* Extract current RVA limits from file header */ +	code_start = pe_header->nt.OptionalHeader.BaseOfCode; +	code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); +#if defined(EFI_TARGET_IA32) +	data_start = pe_header->nt.OptionalHeader.BaseOfData; +#elif defined(EFI_TARGET_X64) +	data_start = code_end; +#endif +	data_mid = ( data_start + +		     pe_header->nt.OptionalHeader.SizeOfInitializedData ); +	data_end = ( data_mid + +		     pe_header->nt.OptionalHeader.SizeOfUninitializedData ); + +	/* Allocate PE section */ +	section_memsz = bfd_section_size ( bfd, section ); +	section_filesz = ( ( flags & SEC_LOAD ) ? +			   efi_file_align ( section_memsz ) : 0 ); +	new = xmalloc ( sizeof ( *new ) + section_filesz ); +	memset ( new, 0, sizeof ( *new ) + section_filesz ); + +	/* Fill in section header details */ +	strncpy ( ( char * ) new->hdr.Name, section->name, +		  sizeof ( new->hdr.Name ) ); +	new->hdr.Misc.VirtualSize = section_memsz; +	new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); +	new->hdr.SizeOfRawData = section_filesz; + +	/* Fill in section characteristics and update RVA limits */ +	if ( flags & SEC_CODE ) { +		/* .text-type section */ +		new->hdr.Characteristics = +			( EFI_IMAGE_SCN_CNT_CODE | +			  EFI_IMAGE_SCN_MEM_NOT_PAGED | +			  EFI_IMAGE_SCN_MEM_EXECUTE | +			  EFI_IMAGE_SCN_MEM_READ ); +		applicable_start = &code_start; +		applicable_end = &code_end; +	} else if ( flags & SEC_DATA ) { +		/* .data-type section */ +		new->hdr.Characteristics = +			( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | +			  EFI_IMAGE_SCN_MEM_NOT_PAGED | +			  EFI_IMAGE_SCN_MEM_READ | +			  EFI_IMAGE_SCN_MEM_WRITE ); +		applicable_start = &data_start; +		applicable_end = &data_mid; +	} else if ( flags & SEC_READONLY ) { +		/* .rodata-type section */ +		new->hdr.Characteristics = +			( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | +			  EFI_IMAGE_SCN_MEM_NOT_PAGED | +			  EFI_IMAGE_SCN_MEM_READ ); +		applicable_start = &data_start; +		applicable_end = &data_mid; +	} else if ( ! ( flags & SEC_LOAD ) ) { +		/* .bss-type section */ +		new->hdr.Characteristics = +			( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | +			  EFI_IMAGE_SCN_MEM_NOT_PAGED | +			  EFI_IMAGE_SCN_MEM_READ | +			  EFI_IMAGE_SCN_MEM_WRITE ); +		applicable_start = &data_mid; +		applicable_end = &data_end; +	} else { +		eprintf ( "Unrecognised characteristics %#lx for section %s\n", +			  flags, section->name ); +		exit ( 1 ); +	} + +	/* Copy in section contents */ +	if ( flags & SEC_LOAD ) { +		if ( ! bfd_get_section_contents ( bfd, section, new->contents, +						  0, section_memsz ) ) { +			eprintf ( "Cannot read section %s: ", section->name ); +			bfd_perror ( NULL ); +			exit ( 1 ); +		} +	} + +	/* Update RVA limits */ +	start = new->hdr.VirtualAddress; +	end = ( start + new->hdr.Misc.VirtualSize ); +	if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) +		*applicable_start = start; +	if ( *applicable_end < end ) +		*applicable_end = end; +	if ( data_start < code_end ) +		data_start = code_end; +	if ( data_mid < data_start ) +		data_mid = data_start; +	if ( data_end < data_mid ) +		data_end = data_mid; + +	/* Write RVA limits back to file header */ +	pe_header->nt.OptionalHeader.BaseOfCode = code_start; +	pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); +#if defined(EFI_TARGET_IA32) +	pe_header->nt.OptionalHeader.BaseOfData = data_start; +#endif +	pe_header->nt.OptionalHeader.SizeOfInitializedData = +		( data_mid - data_start ); +	pe_header->nt.OptionalHeader.SizeOfUninitializedData = +		( data_end - data_mid ); + +	/* Update remaining file header fields */ +	pe_header->nt.FileHeader.NumberOfSections++; +	pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); +	pe_header->nt.OptionalHeader.SizeOfImage = +		efi_file_align ( data_end ); + +	return new; +} + +/** + * Process relocation record + * + * @v bfd		BFD file + * @v section		Section + * @v rel		Relocation entry + * @v pe_reltab		PE relocation table to fill in + */ +static void process_reloc ( bfd *bfd __attribute__ (( unused )), +			    asection *section, arelent *rel, +			    struct pe_relocs **pe_reltab ) { +	reloc_howto_type *howto = rel->howto; +	asymbol *sym = *(rel->sym_ptr_ptr); +	unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + +				 rel->address ); + +	if ( bfd_is_abs_section ( sym->section ) ) { +		/* Skip absolute symbols; the symbol value won't +		 * change when the object is loaded. +		 */ +	} 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 ) ) { +		/* Generate a 4-byte PE relocation */ +		generate_pe_reloc ( pe_reltab, offset, 4 ); +	} else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { +		/* Generate a 2-byte PE relocation */ +		generate_pe_reloc ( pe_reltab, offset, 2 ); +	} else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || +		    ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { +		/* Skip PC-relative relocations; all relative offsets +		 * remain unaltered when the object is loaded. +		 */ +	} else { +		eprintf ( "Unrecognised relocation type %s\n", howto->name ); +		exit ( 1 ); +	} +} + +/** + * Create relocations section + * + * @v pe_header		PE file header + * @v pe_reltab		PE relocation table + * @ret section		Relocation section + */ +static struct pe_section * +create_reloc_section ( struct pe_header *pe_header, +		       struct pe_relocs *pe_reltab ) { +	struct pe_section *reloc; +	size_t section_memsz; +	size_t section_filesz; +	EFI_IMAGE_DATA_DIRECTORY *relocdir; + +	/* Allocate PE section */ +	section_memsz = output_pe_reltab ( pe_reltab, NULL ); +	section_filesz = efi_file_align ( section_memsz ); +	reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); +	memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); + +	/* Fill in section header details */ +	strncpy ( ( char * ) reloc->hdr.Name, ".reloc", +		  sizeof ( reloc->hdr.Name ) ); +	reloc->hdr.Misc.VirtualSize = section_memsz; +	reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; +	reloc->hdr.SizeOfRawData = section_filesz; +	reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | +				       EFI_IMAGE_SCN_MEM_NOT_PAGED | +				       EFI_IMAGE_SCN_MEM_READ ); + +	/* Copy in section contents */ +	output_pe_reltab ( pe_reltab, reloc->contents ); + +	/* Update file header details */ +	pe_header->nt.FileHeader.NumberOfSections++; +	pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); +	pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; +	relocdir = &(pe_header->nt.OptionalHeader.DataDirectory +		     [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); +	relocdir->VirtualAddress = reloc->hdr.VirtualAddress; +	relocdir->Size = reloc->hdr.Misc.VirtualSize; + +	return reloc; +} + +/** + * Create debug section + * + * @v pe_header		PE file header + * @ret section		Debug section + */ +static struct pe_section * +create_debug_section ( struct pe_header *pe_header, const char *filename ) { +	struct pe_section *debug; +	size_t section_memsz; +	size_t section_filesz; +	EFI_IMAGE_DATA_DIRECTORY *debugdir; +	struct { +		EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug; +		EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds; +		char name[ strlen ( filename ) + 1 ]; +	} *contents; + +	/* Allocate PE section */ +	section_memsz = sizeof ( *contents ); +	section_filesz = efi_file_align ( section_memsz ); +	debug = xmalloc ( sizeof ( *debug ) + section_filesz ); +	memset ( debug, 0, sizeof ( *debug ) + section_filesz ); +	contents = ( void * ) debug->contents; + +	/* Fill in section header details */ +	strncpy ( ( char * ) debug->hdr.Name, ".debug", +		  sizeof ( debug->hdr.Name ) ); +	debug->hdr.Misc.VirtualSize = section_memsz; +	debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; +	debug->hdr.SizeOfRawData = section_filesz; +	debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | +				       EFI_IMAGE_SCN_MEM_NOT_PAGED | +				       EFI_IMAGE_SCN_MEM_READ ); + +	/* Create section contents */ +	contents->debug.TimeDateStamp = 0x10d1a884; +	contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; +	contents->debug.SizeOfData = +		( sizeof ( *contents ) - sizeof ( contents->debug ) ); +	contents->debug.RVA = ( debug->hdr.VirtualAddress + +				offsetof ( typeof ( *contents ), rsds ) ); +	contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS; +	snprintf ( contents->name, sizeof ( contents->name ), "%s", +		   filename ); + +	/* Update file header details */ +	pe_header->nt.FileHeader.NumberOfSections++; +	pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); +	pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; +	debugdir = &(pe_header->nt.OptionalHeader.DataDirectory +		     [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); +	debugdir->VirtualAddress = debug->hdr.VirtualAddress; +	debugdir->Size = debug->hdr.Misc.VirtualSize; + +	return debug; +} + +/** + * Write out PE file + * + * @v pe_header		PE file header + * @v pe_sections	List of PE sections + * @v pe		Output file + */ +static void write_pe_file ( struct pe_header *pe_header, +			    struct pe_section *pe_sections, +			    FILE *pe ) { +	struct pe_section *section; +	unsigned long fpos = 0; + +	/* Align length of headers */ +	fpos = pe_header->nt.OptionalHeader.SizeOfHeaders = +		efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders ); + +	/* Assign raw data pointers */ +	for ( section = pe_sections ; section ; section = section->next ) { +		if ( section->hdr.SizeOfRawData ) { +			section->hdr.PointerToRawData = fpos; +			fpos += section->hdr.SizeOfRawData; +			fpos = efi_file_align ( fpos ); +		} +	} + +	/* Write file header */ +	if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) { +		perror ( "Could not write PE header" ); +		exit ( 1 ); +	} + +	/* Write section headers */ +	for ( section = pe_sections ; section ; section = section->next ) { +		if ( fwrite ( §ion->hdr, sizeof ( section->hdr ), +			      1, pe ) != 1 ) { +			perror ( "Could not write section header" ); +			exit ( 1 ); +		} +	} + +	/* Write sections */ +	for ( section = pe_sections ; section ; section = section->next ) { +		if ( fseek ( pe, section->hdr.PointerToRawData, +			     SEEK_SET ) != 0 ) { +			eprintf ( "Could not seek to %x: %s\n", +				  section->hdr.PointerToRawData, +				  strerror ( errno ) ); +			exit ( 1 ); +		} +		if ( section->hdr.SizeOfRawData && +		     ( fwrite ( section->contents, section->hdr.SizeOfRawData, +				1, pe ) != 1 ) ) { +			eprintf ( "Could not write section %.8s: %s\n", +				  section->hdr.Name, strerror ( errno ) ); +			exit ( 1 ); +		} +	} +} + +/** + * Convert ELF to PE + * + * @v elf_name		ELF file name + * @v pe_name		PE file name + */ +static void elf2pe ( const char *elf_name, const char *pe_name, +		     struct options *opts ) { +	char pe_name_tmp[ strlen ( pe_name ) + 1 ]; +	bfd *bfd; +	asymbol **symtab; +	asection *section; +	arelent **reltab; +	arelent **rel; +	struct pe_relocs *pe_reltab = NULL; +	struct pe_section *pe_sections = NULL; +	struct pe_section **next_pe_section = &pe_sections; +	struct pe_header pe_header; +	FILE *pe; + +	/* Create a modifiable copy of the PE name */ +	memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) ); + +	/* Open the file */ +	bfd = open_input_bfd ( elf_name ); +	symtab = read_symtab ( bfd ); + +	/* Initialise the PE header */ +	memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); +	pe_header.nt.OptionalHeader.AddressOfEntryPoint = +		bfd_get_start_address ( bfd ); +	pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; + +	/* For each input section, build an output section and create +	 * the appropriate relocation records +	 */ +	for ( section = bfd->sections ; section ; section = section->next ) { +		/* Discard non-allocatable sections */ +		if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) +			continue; +		/* Create output section */ +		*(next_pe_section) = process_section ( bfd, &pe_header, +						       section ); +		next_pe_section = &(*next_pe_section)->next; +		/* Add relocations from this section */ +		reltab = read_reltab ( bfd, symtab, section ); +		for ( rel = reltab ; *rel ; rel++ ) +			process_reloc ( bfd, section, *rel, &pe_reltab ); +		free ( reltab ); +	} + +	/* Create the .reloc section */ +	*(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); +	next_pe_section = &(*next_pe_section)->next; + +	/* Create the .reloc section */ +	*(next_pe_section) = create_debug_section ( &pe_header, +						    basename ( pe_name_tmp ) ); +	next_pe_section = &(*next_pe_section)->next; + +	/* Write out PE file */ +	pe = fopen ( pe_name, "w" ); +	if ( ! pe ) { +		eprintf ( "Could not open %s for writing: %s\n", +			  pe_name, strerror ( errno ) ); +		exit ( 1 ); +	} +	write_pe_file ( &pe_header, pe_sections, pe ); +	fclose ( pe ); + +	/* Close BFD file */ +	bfd_close ( bfd ); +} + +/** + * Print help + * + * @v program_name	Program name + */ +static void print_help ( const char *program_name ) { +	eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n", +		  program_name ); +} + +/** + * Parse command-line options + * + * @v argc		Argument count + * @v argv		Argument list + * @v opts		Options structure to populate + */ +static int parse_options ( const int argc, char **argv, +			   struct options *opts ) { +	char *end; +	int c; + +	while (1) { +		int option_index = 0; +		static struct option long_options[] = { +			{ "subsystem", required_argument, NULL, 's' }, +			{ "help", 0, NULL, 'h' }, +			{ 0, 0, 0, 0 } +		}; + +		if ( ( c = getopt_long ( argc, argv, "s:h", +					 long_options, +					 &option_index ) ) == -1 ) { +			break; +		} + +		switch ( c ) { +		case 's': +			opts->subsystem = strtoul ( optarg, &end, 0 ); +			if ( *end ) { +				eprintf ( "Invalid subsytem \"%s\"\n", +					  optarg ); +				exit ( 2 ); +			} +			break; +		case 'h': +			print_help ( argv[0] ); +			exit ( 0 ); +		case '?': +		default: +			exit ( 2 ); +		} +	} +	return optind; +} + +int main ( int argc, char **argv ) { +	struct options opts = { +		.subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION, +	}; +	int infile_index; +	const char *infile; +	const char *outfile; + +	/* Initialise libbfd */ +	bfd_init(); + +	/* Parse command-line arguments */ +	infile_index = parse_options ( argc, argv, &opts ); +	if ( argc != ( infile_index + 2 ) ) { +		print_help ( argv[0] ); +		exit ( 2 ); +	} +	infile = argv[infile_index]; +	outfile = argv[infile_index + 1]; + +	/* Convert file */ +	elf2pe ( infile, outfile, &opts ); + +	return 0; +} diff --git a/roms/ipxe/src/util/fixrom.pl b/roms/ipxe/src/util/fixrom.pl new file mode 100755 index 00000000..dcc38fe4 --- /dev/null +++ b/roms/ipxe/src/util/fixrom.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +my @romfiles = @ARGV; + +foreach my $romfile ( @romfiles ) { +  my $rom = new Option::ROM; +  $rom->load ( $romfile ); +  my $image = $rom; +  while ( $image ) { +    $image->pnp_header->fix_checksum() if $image->pnp_header; +    $image->undi_header->fix_checksum() if $image->undi_header; +    $image->ipxe_header->fix_checksum() if $image->ipxe_header; +    $image->fix_checksum(); +    $image = $image->next_image(); +  } +  $rom->save ( $romfile ); +} diff --git a/roms/ipxe/src/util/fnrec.pl b/roms/ipxe/src/util/fnrec.pl new file mode 100755 index 00000000..cc7312b6 --- /dev/null +++ b/roms/ipxe/src/util/fnrec.pl @@ -0,0 +1,146 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +=head1 NAME + +fnrec.pl + +=head1 SYNOPSIS + +fnrec.pl [options] bin/image.xxx < logfile + +Decode a function trace produced by building with FNREC=1 + +Options: + +	-m,--max-depth=N	Set maximum displayed function depth + +=cut + +use IPC::Open2; +use Getopt::Long; +use Pod::Usage; +use strict; +use warnings; + +use constant MAX_OPEN_BRACE => 10; +use constant MAX_COMMON_BRACE => 3; +use constant MAX_CLOSE_BRACE => 10; + +# Parse command-line options +my $max_depth = 16; +Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); +GetOptions ( +  'help|h' => sub { pod2usage ( 1 ); }, +  'max-depth|m=i' => sub { shift; $max_depth = shift; }, +) or die "Could not parse command-line options\n"; +pod2usage ( 1 ) unless @ARGV == 1; +my $image = shift; +my $elf = $image.".tmp"; +die "ELF file ".$elf." not found\n" unless -e $elf; + +# Start up addr2line +my $addr2line_pid = open2 ( my $addr2line_out, my $addr2line_in, +			    "addr2line", "-f", "-e", $elf ) +    or die "Could not start addr2line: $!\n"; + +# Translate address using addr2line +sub addr2line { +  my $address = shift; + +  print $addr2line_in $address."\n"; +  chomp ( my $name = <$addr2line_out> ); +  chomp ( my $file_line = <$addr2line_out> ); +  ( my $file, my $line ) = ( $file_line =~ /^(.*):(\d+)$/ ); +  $file =~ s/^.*\/src\///; +  my $location = ( $line ? $file.":".$line." = ".$address : $address ); +  return ( $name, $location ); +} + +# Parse logfile +my $depth = 0; +my $depths = []; +while ( my $line = <> ) { +  chomp $line; +  $line =~ s/\r//g; +  ( my $called_fn, my $call_site, my $entry_count, my $exit_count ) = +      ( $line =~ /^(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+([0-9]+)\s+([0-9]+)$/ ) +      or print $line."\n" and next; + +  ( my $called_fn_name, undef ) = addr2line ( $called_fn ); +  ( undef, my $call_site_location ) = addr2line ( $call_site ); +  $entry_count = ( $entry_count + 0 ); +  $exit_count = ( $exit_count + 0 ); + +  if ( $entry_count >= $exit_count ) { +    # +    # Function entry +    # +    my $text = ""; +    $text .= $called_fn_name." (from ".$call_site_location.")"; +    if ( $exit_count <= MAX_COMMON_BRACE ) { +      $text .= " { }" x $exit_count; +    } else { +      $text .= " { } x ".$exit_count; +    } +    $entry_count -= $exit_count; +    if ( $entry_count <= MAX_OPEN_BRACE ) { +      $text .= " {" x $entry_count; +    } else { +      $text .= " { x ".$entry_count; +    } +    my $indent = "  " x $depth; +    print $indent.$text."\n"; +    $depth += $entry_count; +    $depth = $max_depth if ( $depth > $max_depth ); +    push @$depths, ( { called_fn => $called_fn, call_site => $call_site } ) x +	( $depth - @$depths ); +  } else { +    # +    # Function exit +    # +    my $text = ""; +    if ( $entry_count <= MAX_COMMON_BRACE ) { +      $text .= " { }" x $entry_count; +    } else { +      $text .= " { } x ".$entry_count; +    } +    $exit_count -= $entry_count; +    if ( $exit_count <= MAX_CLOSE_BRACE ) { +      $text .= " }" x $exit_count; +    } else { +      $text .= " } x ".$exit_count; +    } +    $depth -= $exit_count; +    $depth = 0 if ( $depth < 0 ); +    if ( ( @$depths == 0 ) || +	 ( $depths->[$depth]->{called_fn} ne $called_fn ) || +	 ( $depths->[$depth]->{call_site} ne $call_site ) ) { +      $text .= " (from ".$called_fn_name." to ".$call_site_location.")"; +    } +    splice ( @$depths, $depth ); +    my $indent = "  " x $depth; +    print substr ( $indent.$text, 1 )."\n"; +  } +} + +# Clean up addr2line +close $addr2line_in; +close $addr2line_out; +waitpid ( $addr2line_pid, 0 ); diff --git a/roms/ipxe/src/util/geniso b/roms/ipxe/src/util/geniso new file mode 100755 index 00000000..521c929e --- /dev/null +++ b/roms/ipxe/src/util/geniso @@ -0,0 +1,142 @@ +#!/bin/bash +# +# Generate a isolinux ISO boot image + +function help() { +	echo "usage: ${0} [OPTIONS] foo.lkrn [bar.lkrn,...]" +	echo +	echo "where OPTIONS are:" +	echo " -h       show this help" +	echo " -l       build legacy image with floppy emulation" +	echo " -o FILE  save iso image to file" +} + +LEGACY=0 +FIRST="" + +while getopts "hlo:" opt; do +	case ${opt} in +		h) +			help +			exit 0 +			;; +		l) +			LEGACY=1 +			;; +		o) +			OUT="${OPTARG}" +			;; +	esac +done + +shift $((OPTIND - 1)) + +if [ -z "${OUT}" ]; then +	echo "${0}: no output file given" >&2 +	help +	exit 1 +fi + +# There should either be mkisofs or the compatible genisoimage program +for command in genisoimage mkisofs; do +	if ${command} --version >/dev/null 2>/dev/null; then +		mkisofs=(${command}) +		break +	fi +done + +if [ -z "${mkisofs}" ]; then +	echo "${0}: mkisofs or genisoimage not found, please install or set PATH" >&2 +	exit 1 +fi + +dir=$(mktemp -d bin/iso.dir.XXXXXX) +cfg=${dir}/isolinux.cfg + +mkisofs+=(-quiet -l -volid "iPXE" -preparer "iPXE build system" +	-appid "iPXE ${VERSION} - Open Source Network Boot Firmware" +	-publisher "http://ipxe.org/" -c boot.cat) + +# generate the config +cat > ${cfg} <<EOF +# These default options can be changed in the geniso script +SAY iPXE ISO boot image +TIMEOUT 30 +EOF +for f; do +	if [ ! -r ${f} ]; then +		echo "${f} does not exist, skipping" >&2 +		continue +	fi +	b=$(basename ${f}) +	g=${b%.lkrn} +	g=${g//[^a-z0-9]} +	g=${g:0:8}.krn +	case "${FIRST}" in +		"") +			echo "DEFAULT ${b}" +			FIRST=${g} +			;; +	esac +	echo "LABEL ${b}" +	echo " KERNEL ${g}" +	cp ${f} ${dir}/${g} +done >> ${cfg} + +case "${LEGACY}" in +	1) +		# check for mtools +		case "$(mtools -V)" in +			Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*) +				;; +			*) +				echo "Mtools version 3.9.9 or later is required" >&2 +				exit 1 +				;; +		esac + +		# generate floppy image +		img=${dir}/boot.img +		mformat -f 1440 -C -i ${img} :: + +		# copy lkrn file to floppy image +		for f in ${dir}/*.krn; do +			mcopy -m -i ${img} ${f} ::$(basename ${g}) +			rm -f ${f} +		done + +		# copy config file to floppy image +		mcopy -i ${img} ${cfg} ::syslinux.cfg +		rm -f ${cfg} + +		# write syslinux bootloader to floppy image +		if ! syslinux ${img}; then +			echo "${0}: failed writing syslinux to floppy image ${img}" >&2 +			exit 1 +		fi + +		# generate the iso image +		"${mkisofs[@]}" -b boot.img -output ${OUT} ${dir} +		;; +	0) +		# copy isolinux bootloader +		cp ${ISOLINUX_BIN} ${dir} + +		# syslinux 6.x needs a file called ldlinux.c32 +		LDLINUX_C32=$(dirname ${ISOLINUX_BIN})/ldlinux.c32 +		if [ -s ${LDLINUX_C32} ]; then +			cp ${LDLINUX_C32} ${dir} +		fi + +		# generate the iso image +		"${mkisofs[@]}" -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -output ${OUT} ${dir} + +		# isohybrid will be used if available +		if isohybrid --version >/dev/null 2>/dev/null; then +			isohybrid ${OUT} >/dev/null +		fi +		;; +esac + +# clean up temporary dir +rm -fr ${dir} diff --git a/roms/ipxe/src/util/genkeymap.pl b/roms/ipxe/src/util/genkeymap.pl new file mode 100755 index 00000000..7a5024bf --- /dev/null +++ b/roms/ipxe/src/util/genkeymap.pl @@ -0,0 +1,238 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +=head1 NAME + +genkeymap.pl + +=head1 SYNOPSIS + +genkeymap.pl [options] <keymap name> + +Options: + +    -f,--from=<name>	Set BIOS keymap name (default "us") +    -h,--help		Display brief help message +    -v,--verbose	Increase verbosity +    -q,--quiet		Decrease verbosity + +=cut + +# With reference to: +# +# http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html + +use Getopt::Long; +use Pod::Usage; +use strict; +use warnings; + +use constant BIOS_KEYMAP => "us"; +use constant BKEYMAP_MAGIC => "bkeymap"; +use constant MAX_NR_KEYMAPS => 256; +use constant NR_KEYS => 128; +use constant KG_SHIFT => 0; +use constant KG_ALTGR => 1; +use constant KG_CTRL => 2; +use constant KG_ALT => 3; +use constant KG_SHIFTL => 4; +use constant KG_KANASHIFT => 4; +use constant KG_SHIFTR => 5; +use constant KG_CTRLL => 6; +use constant KG_CTRLR => 7; +use constant KG_CAPSSHIFT => 8; +use constant KT_LATIN => 0; +use constant KT_FN => 1; +use constant KT_SPEC => 2; +use constant KT_PAD => 3; +use constant KT_DEAD => 4; +use constant KT_CONS => 5; +use constant KT_CUR => 6; +use constant KT_SHIFT => 7; +use constant KT_META => 8; +use constant KT_ASCII => 9; +use constant KT_LOCK => 10; +use constant KT_LETTER => 11; +use constant KT_SLOCK => 12; +use constant KT_SPKUP => 14; + +my $verbosity = 1; +my $from_name = BIOS_KEYMAP; + +# Read named keymaps using "loadkeys -b" +# +sub read_keymaps { +  my $name = shift; +  my $keymaps = []; + +  # Generate binary keymap +  open my $pipe, "-|", "loadkeys", "-b", $name +      or die "Could not load keymap \"".$name."\": $!\n"; + +  # Check magic +  read $pipe, my $magic, length BKEYMAP_MAGIC +      or die "Could not read from \"".$name."\": $!\n"; +  die "Bad magic value from \"".$name."\"\n" +      unless $magic eq BKEYMAP_MAGIC; + +  # Read list of included keymaps +  read $pipe, my $included, MAX_NR_KEYMAPS +      or die "Could not read from \"".$name."\": $!\n"; +  my @included = unpack ( "C*", $included ); +  die "Missing or truncated keymap list from \"".$name."\"\n" +      unless @included == MAX_NR_KEYMAPS; + +  # Read each keymap in turn +  for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS ; $keymap++ ) { +    if ( $included[$keymap] ) { +      read $pipe, my $keysyms, ( NR_KEYS * 2 ) +	  or die "Could not read from \"".$name."\": $!\n"; +      my @keysyms = unpack ( "S*", $keysyms ); +      die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n" +	  unless @keysyms == NR_KEYS; +      push @$keymaps, \@keysyms; +    } else { +      push @$keymaps, undef; +    } +  } + +  close $pipe; +  return $keymaps; +} + +# Translate keysym value to ASCII +# +sub keysym_to_ascii { +  my $keysym = shift; + +  # Non-existent keysyms have no ASCII equivalent +  return unless $keysym; + +  # Sanity check +  if ( $keysym & 0xf000 ) { +    warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n"; +    return; +  } + +  # Extract type and value +  my $type = ( $keysym >> 8 ); +  my $value = ( $keysym & 0xff ); + +  # Non-simple types have no ASCII equivalent +  return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) || +		  ( $type == KT_LETTER ) ); + +  # High-bit-set characters cannot be generated on a US keyboard +  return if $value & 0x80; + +  return $value; +} + +# Translate ASCII to descriptive name +# +sub ascii_to_name { +  my $ascii = shift; + +  if ( $ascii == 0x5c ) { +    return "'\\\\'"; +  } elsif ( $ascii == 0x27 ) { +    return "'\\\''"; +  } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) { +    return sprintf ( "'%c'", $ascii ); +  } elsif ( $ascii <= 0x1a ) { +    return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) ); +  } else { +    return sprintf ( "0x%02x", $ascii ); +  } +} + +# Produce translation table between two keymaps +# +sub translate_keymaps { +  my $from = shift; +  my $to = shift; +  my $map = {}; + +  foreach my $keymap ( 0, 1 << KG_SHIFT, 1 << KG_CTRL ) { +    for ( my $keycode = 0 ; $keycode < NR_KEYS ; $keycode++ ) { +      my $from_ascii = keysym_to_ascii ( $from->[$keymap]->[$keycode] ) +	  or next; +      my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] ) +	  or next; +      my $new_map = ( ! exists $map->{$from_ascii} ); +      my $update_map = +	  ( $new_map || ( $keycode < $map->{$from_ascii}->{keycode} ) ); +      if ( ( $verbosity > 1 ) && +	   ( ( $from_ascii != $to_ascii ) || +	     ( $update_map && ! $new_map ) ) ) { +	printf STDERR "In keymap %d: %s => %s%s\n", $keymap, +	       ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ), +	       ( $update_map ? ( $new_map ? "" : " (override)" ) +			     : " (ignored)" ); +      } +      if ( $update_map ) { +	$map->{$from_ascii} = { +	  to_ascii => $to_ascii, +	  keycode => $keycode, +	}; +      } +    } +  } +  return { map { $_ => $map->{$_}->{to_ascii} } keys %$map }; +} + +# Parse command-line options +Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); +GetOptions ( +  'verbose|v+' => sub { $verbosity++; }, +  'quiet|q+' => sub { $verbosity--; }, +  'from|f=s' => sub { shift; $from_name = shift; }, +  'help|h' => sub { pod2usage ( 1 ); }, +) or die "Could not parse command-line options\n"; +pod2usage ( 1 ) unless @ARGV == 1; +my $to_name = shift; + +# Read and translate keymaps +my $from = read_keymaps ( $from_name ); +my $to = read_keymaps ( $to_name ); +my $map = translate_keymaps ( $from, $to ); + +# Generate output +( my $to_name_c = $to_name ) =~ s/\W/_/g; +printf "/** \@file\n"; +printf " *\n"; +printf " * \"".$to_name."\" keyboard mapping\n"; +printf " *\n"; +printf " * This file is automatically generated; do not edit\n"; +printf " *\n"; +printf " */\n"; +printf "\n"; +printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n"; +printf "\n"; +printf "#include <ipxe/keymap.h>\n"; +printf "\n"; +printf "/** \"".$to_name."\" keyboard mapping */\n"; +printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n"; +foreach my $from_sym ( sort { $a <=> $b } keys %$map ) { +  my $to_sym = $map->{$from_sym}; +  next if $from_sym == $to_sym; +  printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym, +	 ascii_to_name ( $from_sym ), ascii_to_name ( $to_sym ); +} +printf "};\n"; diff --git a/roms/ipxe/src/util/gensdsk b/roms/ipxe/src/util/gensdsk new file mode 100755 index 00000000..9e8361d4 --- /dev/null +++ b/roms/ipxe/src/util/gensdsk @@ -0,0 +1,65 @@ +#!/bin/bash +# +# Generate a syslinux floppy that loads a iPXE image +# +# gensdsk foo.sdsk foo.lkrn +# +# the floppy image is the first argument +#   followed by list of .lkrn images +# + +case $# in +0|1) +	echo Usage: $0 foo.sdsk foo.lkrn ... +	exit 1 +	;; +esac +case "`mtools -V`" in +Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*) +	;; +*) +	echo Mtools version 3.9.9 or later is required +	exit 1 +	;; +esac +img=$1 +shift +dir=`mktemp -d bin/sdsk.dir.XXXXXX` + +mformat -f 1440 -C -i $img :: +cfg=$dir/syslinux.cfg +cat > $cfg <<EOF + +# These default options can be changed in the gensdsk script +TIMEOUT 30 +EOF +first= +for f +do +	if [ ! -r $f ] +	then +		echo $f does not exist, skipping 1>&2 +		continue +	fi +	# shorten name for 8.3 filesystem +	b=$(basename $f) +	g=${b%.lkrn} +	g=${g//[^a-z0-9]} +	g=${g:0:8}.krn +	case "$first" in +	"") +		echo DEFAULT $g +		;; +	esac +	first=$g +	echo LABEL $b +	echo "" KERNEL $g +	mcopy -m -i $img $f ::$g +done >> $cfg +mcopy -i $img $cfg ::syslinux.cfg +if ! syslinux $img +then +	exit 1 +fi + +rm -fr $dir diff --git a/roms/ipxe/src/util/get-pci-ids b/roms/ipxe/src/util/get-pci-ids new file mode 100755 index 00000000..42466221 --- /dev/null +++ b/roms/ipxe/src/util/get-pci-ids @@ -0,0 +1,136 @@ +#! /usr/bin/perl -w + +# get-pci-ids: extract pci vendor/device ids from linux net drivers + +# Copyright (C) 2003 Georg Baum <gbaum@users.sf.net> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +# Known bugs/limitations: +# - Does not recognize all drivers because some require special cflags. +#   Fails also on some drivers that do belong to other architectures +#   than the one of the machine this script is running on. +#   This is currently not so important because all drivers that have an +#   Etherboot counterpart are recognized. + + +use strict; +use File::Basename "dirname"; +use POSIX "uname"; + +# Where to find the kernel sources +my $kernel_src = "/usr/src/linux"; + +if($#ARGV >= 0) { +	$kernel_src = shift; +} + +# Sanity checks +if($#ARGV >= 0) { +	print STDERR "Too many arguments.\n"; +	print STDERR "Usage: get-pci-ids [path to kernel sources]\n"; +	print STDERR "       /usr/src/linux is assumed if no path is given.\n"; +	exit 1; +} + +unless(-f "$kernel_src/include/linux/version.h") { +	print STDERR "Could not find $kernel_src/include/linux/version.h.\n"; +	print STDERR "$kernel_src is probably no Linux kernel source tree.\n"; +	exit 1; +} + +# Flags that are needed to preprocess the drivers. +# Some drivers need optimization +my $cflags="-D__KERNEL__ -I$kernel_src/include -I$kernel_src/net/inet -O2"; + +# The C preprocessor. It needs to spit out the preprocessed source on stdout. +my $cpp="gcc -E"; + +# List of drivers. We parse every .c file. It does not harm if it does not contain a driver. +my @drivers = split /\s+/, `find $kernel_src/drivers/net -name '*.c' | sort`; + +# Kernel version +my $version = `grep UTS_RELEASE $kernel_src/include/linux/version.h`; +chomp $version; +$version =~ s/\s*#define\s+UTS_RELEASE\s+"(\S+)".*$/$1/g; + +# Architecture +my @uname = uname(); + + +# Print header +print "# PCI vendor/device ids extracted from Linux $version on $uname[4] at " . gmtime() . "\n"; + +my $driver; + +# Process the drivers +foreach $driver (@drivers) { + +	# Preprocess to expand macros +	my $command = "$cpp $cflags -I" . dirname($driver) . " $driver"; +	open  DRIVER, "$command |" or die "Could not execute\n\"$command\".\n"; + +	# Extract the pci_device_id structure +	my $found = 0; +	my $line = ""; +	my @lines; +	while(<DRIVER>) { +		if(/^\s*static\s+struct\s+pci_device_id/) { +			# This file contains a driver. Print the name. +			$driver =~ s!$kernel_src/drivers/net/!!g; +			print "\n$driver\n"; +			$found = 1; +			next; +		} +		if($found == 1){ +			if(/\};/ or /{\s*0\s*,?\s*}/) { +				# End of struct +				$found = 0; +			} else { +				chomp; +				if(/\}\s*,?\s*\n?$/) { +					# This line contains a full entry or the last part of it. +					$_ = $line . $_; +					$line = ""; +					s/[,\{\};\(\)]//g;	# Strip punctuation +					s/^\s+//g;		# Eat whitespace at beginning of line +					tr[A-Z][a-z];		# Convert to lowercase +					# Push the vendor and device id to @lines if this line is not empty. +					# We ignore everything else that might be there +					my ($vendor_id, $device_id, $remainder) = split /\W+/, $_, 3; +					push @lines, "$vendor_id $device_id\n" if($vendor_id && $device_id); +				} else { +					# This line does contain a partial entry. Remember it. +					$line .= "$_ "; +				} +			} +		} +	} +	close DRIVER;		# No "or die", because $cpp fails on some files + +	# Now print out the sorted values +	@lines = sort @lines; +	my $lastline = ""; +	foreach(@lines) { +		# Print each vendor/device id combination only once. +		# Some drivers (e.g. e100) do contain subfamilies +		print if($_ ne $lastline); +		$lastline = $_; +	} +} + + diff --git a/roms/ipxe/src/util/hijack.c b/roms/ipxe/src/util/hijack.c new file mode 100644 index 00000000..ed89592b --- /dev/null +++ b/roms/ipxe/src/util/hijack.c @@ -0,0 +1,628 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <signal.h> +#include <net/if.h> +#include <net/ethernet.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <syslog.h> +#include <getopt.h> +#include <pcap.h> + +#define SNAPLEN 1600 + +/* + * FIXME: is there a way to detect the version of the libpcap library? + * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report + * their version number as 2.4. + */ +#define HAVE_PCAP_INJECT 0 + +struct hijack { +	pcap_t *pcap; +	int fd; +	int datalink; +	int filtered; +	unsigned long rx_count; +	unsigned long tx_count; +}; + +struct hijack_listener { +	struct sockaddr_un sun; +	int fd; +}; + +struct hijack_options { +	char interface[IF_NAMESIZE]; +	int daemonise; +}; + +static int daemonised = 0; + +static int signalled = 0; + +static void flag_signalled ( int signal __attribute__ (( unused )) ) { +	signalled = 1; +} + +#if ! HAVE_PCAP_INJECT +/** + * Substitute for pcap_inject(), if this version of libpcap doesn't + * have it.  Will almost certainly only work under Linux. + * + */ +int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) { +	int fd; +	char *errbuf = pcap_geterr ( pcap ); + +	fd = pcap_get_selectable_fd ( pcap ); +	if ( fd < 0 ) { +		snprintf ( errbuf, PCAP_ERRBUF_SIZE, +			   "could not get file descriptor" ); +		return -1; +	} +	if ( write ( fd, data, len ) != len ) { +		snprintf ( errbuf, PCAP_ERRBUF_SIZE, +			   "could not write data: %s", strerror ( errno ) ); +		return -1; +	} +	return len; +} +#endif /* ! HAVE_PCAP_INJECT */ + +/** + * Log error message + * + */ +static __attribute__ (( format ( printf, 2, 3 ) )) void +logmsg ( int level, const char *format, ... ) { +	va_list ap; + +	va_start ( ap, format ); +	if ( daemonised ) { +		vsyslog ( ( LOG_DAEMON | level ), format, ap ); +	} else { +		vfprintf ( stderr, format, ap ); +	} +	va_end ( ap ); +} + +/** + * Open pcap device + * + */ +static int hijack_open ( const char *interface, struct hijack *hijack ) { +	char errbuf[PCAP_ERRBUF_SIZE]; + +	/* Open interface via pcap */ +	errbuf[0] = '\0'; +	hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf ); +	if ( ! hijack->pcap ) { +		logmsg ( LOG_ERR, "Failed to open %s: %s\n", +			 interface, errbuf ); +		goto err; +	} +	if ( errbuf[0] ) +		logmsg ( LOG_WARNING, "Warning: %s\n", errbuf ); + +	/* Set capture interface to non-blocking mode */ +	if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) { +		logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n", +			 interface, errbuf ); +		goto err; +	} + +	/* Get file descriptor for select() */ +	hijack->fd = pcap_get_selectable_fd ( hijack->pcap ); +	if ( hijack->fd < 0 ) { +		logmsg ( LOG_ERR, "Cannot get selectable file descriptor " +			 "for %s\n", interface ); +		goto err; +	} + +	/* Get link layer type */ +	hijack->datalink = pcap_datalink ( hijack->pcap ); + +	return 0; + + err: +	if ( hijack->pcap ) +		pcap_close ( hijack->pcap ); +	return -1; +} + +/** + * Close pcap device + * + */ +static void hijack_close ( struct hijack *hijack ) { +	pcap_close ( hijack->pcap ); +} + +/** + * Install filter for hijacked connection + * + */ +static int hijack_install_filter ( struct hijack *hijack, +				   char *filter ) { +	struct bpf_program program; + +	/* Compile filter */ +	if ( pcap_compile ( hijack->pcap, &program, filter, 1, 0 ) < 0 ) { +		logmsg ( LOG_ERR, "could not compile filter \"%s\": %s\n", +			 filter, pcap_geterr ( hijack->pcap ) ); +		goto err_nofree; +	} + +	/* Install filter */ +	if ( pcap_setfilter ( hijack->pcap, &program ) < 0 ) { +		logmsg ( LOG_ERR, "could not install filter \"%s\": %s\n", +			 filter, pcap_geterr ( hijack->pcap ) ); +		goto err; +	} +	 +	logmsg ( LOG_INFO, "using filter \"%s\"\n", filter ); + +	pcap_freecode ( &program ); +	return 0; + + err:	 +	pcap_freecode ( &program ); + err_nofree: +	return -1; +} + +/** + * Set up filter for hijacked ethernet connection + * + */ +static int hijack_filter_ethernet ( struct hijack *hijack, const char *buf, +				    size_t len ) { +	char filter[55]; /* see format string */ +	struct ether_header *ether_header = ( struct ether_header * ) buf; +	unsigned char *hwaddr = ether_header->ether_shost; + +	if ( len < sizeof ( *ether_header ) ) +		return -1; + +	snprintf ( filter, sizeof ( filter ), "broadcast or multicast or " +		   "ether host %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], +		   hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] ); + +	return hijack_install_filter ( hijack, filter ); +} + +/** + * Set up filter for hijacked connection + * + */ +static int hijack_filter ( struct hijack *hijack, const char *buf, +			   size_t len ) { +	switch ( hijack->datalink ) { +	case DLT_EN10MB: +		return hijack_filter_ethernet ( hijack, buf, len ); +	default: +		logmsg ( LOG_ERR, "unsupported protocol %s: cannot filter\n", +			 ( pcap_datalink_val_to_name ( hijack->datalink ) ? +			   pcap_datalink_val_to_name ( hijack->datalink ) : +			   "UNKNOWN" ) ); +		/* Return success so we don't get called again */ +		return 0; +	} +} + +/** + * Forward data from hijacker + * + */ +static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) { +	char buf[SNAPLEN]; +	ssize_t len; + +	/* Read packet from hijacker */ +	len = read ( fd, buf, sizeof ( buf ) ); +	if ( len < 0 ) { +		logmsg ( LOG_ERR, "read from hijacker failed: %s\n", +			 strerror ( errno ) ); +		return -1; +	} +	if ( len == 0 ) +		return 0; + +	/* Set up filter if not already in place */ +	if ( ! hijack->filtered ) { +		if ( hijack_filter ( hijack, buf, len ) == 0 ) +			hijack->filtered = 1; +	} + +	/* Transmit packet to network */ +	if ( pcap_inject ( hijack->pcap, buf, len ) != len ) { +		logmsg ( LOG_ERR, "write to hijacked port failed: %s\n", +			 pcap_geterr ( hijack->pcap ) ); +		return -1; +	} + +	hijack->tx_count++; +	return len; +}; + +/** + * Forward data to hijacker + * + */ +static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) { +	struct pcap_pkthdr *pkt_header; +	const unsigned char *pkt_data; +	ssize_t len; + +	/* Receive packet from network */ +	if ( pcap_next_ex ( hijack->pcap, &pkt_header, &pkt_data ) < 0 ) { +		logmsg ( LOG_ERR, "read from hijacked port failed: %s\n", +			 pcap_geterr ( hijack->pcap ) ); +		return -1; +	} +	if ( pkt_header->caplen != pkt_header->len ) { +		logmsg ( LOG_ERR, "read partial packet (%d of %d bytes)\n", +			 pkt_header->caplen, pkt_header->len ); +		return -1; +	} +	if ( pkt_header->caplen == 0 ) +		return 0; +	len = pkt_header->caplen; + +	/* Write packet to hijacker */ +	if ( write ( fd, pkt_data, len ) != len ) { +		logmsg ( LOG_ERR, "write to hijacker failed: %s\n", +			 strerror ( errno ) ); +		return -1; +	} + +	hijack->rx_count++; +	return len; +}; + + +/** + * Run hijacker + * + */ +static int run_hijacker ( const char *interface, int fd ) { +	struct hijack hijack; +	fd_set fdset; +	int max_fd; +	ssize_t len; + +	logmsg ( LOG_INFO, "new connection for %s\n", interface ); + +	/* Open connection to network */ +	memset ( &hijack, 0, sizeof ( hijack ) ); +	if ( hijack_open ( interface, &hijack ) < 0 ) +		goto err; +	 +	/* Do the forwarding */ +	max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd ); +	while ( 1 ) { +		/* Wait for available data */ +		FD_ZERO ( &fdset ); +		FD_SET ( fd, &fdset ); +		FD_SET ( hijack.fd, &fdset ); +		if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) { +			logmsg ( LOG_ERR, "select failed: %s\n", +				 strerror ( errno ) ); +			goto err; +		} +		if ( FD_ISSET ( fd, &fdset ) ) { +			len = forward_from_hijacker ( &hijack, fd ); +			if ( len < 0 ) +				goto err; +			if ( len == 0 ) +				break; +		} +		if ( FD_ISSET ( hijack.fd, &fdset ) ) { +			len = forward_to_hijacker ( fd, &hijack ); +			if ( len < 0 ) +				goto err; +			if ( len == 0 ) +				break; +		} +	} + +	hijack_close ( &hijack ); +	logmsg ( LOG_INFO, "closed connection for %s\n", interface ); +	logmsg ( LOG_INFO, "received %ld packets, sent %ld packets\n", +		 hijack.rx_count, hijack.tx_count ); + +	return 0; + + err: +	if ( hijack.pcap ) +		hijack_close ( &hijack ); +	return -1; +} + +/** + * Open listener socket + * + */ +static int open_listener ( const char *interface, +			   struct hijack_listener *listener ) { +	 +	/* Create socket */ +	listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 ); +	if ( listener->fd < 0 ) { +		logmsg ( LOG_ERR, "Could not create socket: %s\n", +			 strerror ( errno ) ); +		goto err; +	} + +	/* Bind to local filename */ +	listener->sun.sun_family = AF_UNIX, +	snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ), +		   "/var/run/hijack-%s", interface ); +	if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun, +		    sizeof ( listener->sun ) ) < 0 ) { +		logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n", +			 listener->sun.sun_path, strerror ( errno ) ); +		goto err; +	} + +	/* Set as a listening socket */ +	if ( listen ( listener->fd, 0 ) < 0 ) { +		logmsg ( LOG_ERR, "Could not listen to %s: %s\n", +			 listener->sun.sun_path, strerror ( errno ) ); +		goto err; +	} + +	return 0; +	 + err: +	if ( listener->fd >= 0 ) +		close ( listener->fd ); +	return -1; +} + +/** + * Listen on listener socket + * + */ +static int listen_for_hijackers ( struct hijack_listener *listener, +				  const char *interface ) { +	int fd; +	pid_t child; +	int rc; + +	logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path ); + +	while ( ! signalled ) { +		/* Accept new connection, interruptibly */ +		siginterrupt ( SIGINT, 1 ); +		siginterrupt ( SIGHUP, 1 ); +		fd = accept ( listener->fd, NULL, 0 ); +		siginterrupt ( SIGINT, 0 ); +		siginterrupt ( SIGHUP, 0 ); +		if ( fd < 0 ) { +			if ( errno == EINTR ) { +				continue; +			} else { +				logmsg ( LOG_ERR, "accept failed: %s\n", +					 strerror ( errno ) ); +				goto err; +			} +		} + +		/* Fork child process */ +		child = fork(); +		if ( child < 0 ) { +			logmsg ( LOG_ERR, "fork failed: %s\n", +				 strerror ( errno ) ); +			goto err; +		} +		if ( child == 0 ) { +			/* I am the child; run the hijacker */ +			rc = run_hijacker ( interface, fd ); +			close ( fd ); +			exit ( rc ); +		} +		 +		close ( fd ); +	} + +	logmsg ( LOG_INFO, "Stopped listening on %s\n", +		 listener->sun.sun_path ); +	return 0; + + err: +	if ( fd >= 0 ) +		close ( fd ); +	return -1; +} + +/** + * Close listener socket + * + */ +static void close_listener ( struct hijack_listener *listener ) { +	close ( listener->fd ); +	unlink ( listener->sun.sun_path ); +} + +/** + * Print usage + * + */ +static void usage ( char **argv ) { +	logmsg ( LOG_ERR, +		 "Usage: %s [options]\n" +		 "\n" +		 "Options:\n" +		 "  -h|--help               Print this help message\n" +		 "  -i|--interface intf     Use specified network interface\n" +		 "  -n|--nodaemon           Run in foreground\n", +		 argv[0] ); +} + +/** + * Parse command-line options + * + */ +static int parse_options ( int argc, char **argv, +			   struct hijack_options *options ) { +	static struct option long_options[] = { +		{ "interface", 1, NULL, 'i' }, +		{ "nodaemon", 0, NULL, 'n' }, +		{ "help", 0, NULL, 'h' }, +		{ }, +	}; +	int c; + +	/* Set default options */ +	memset ( options, 0, sizeof ( *options ) ); +	strncpy ( options->interface, "eth0", sizeof ( options->interface ) ); +	options->daemonise = 1; + +	/* Parse command-line options */ +	while ( 1 ) { +		int option_index = 0; +		 +		c = getopt_long ( argc, argv, "i:hn", long_options, +				  &option_index ); +		if ( c < 0 ) +			break; + +		switch ( c ) { +		case 'i': +			strncpy ( options->interface, optarg, +				  sizeof ( options->interface ) ); +			break; +		case 'n': +			options->daemonise = 0; +			break; +		case 'h': +			usage( argv ); +			return -1; +		case '?': +			/* Unrecognised option */ +			return -1; +		default: +			logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c ); +			return -1; +		} +	} + +	/* Check there's nothing left over on the command line */ +	if ( optind != argc ) { +		usage ( argv ); +		return -1; +	} + +	return 0; +} + +/** + * Daemonise + * + */ +static int daemonise ( const char *interface ) { +	char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-<intf>.pid" */ +	char pid[16]; +	int pidlen; +	int fd = -1; + +	/* Daemonise */ +	if ( daemon ( 0, 0 ) < 0 ) { +		logmsg ( LOG_ERR, "Could not daemonise: %s\n", +			 strerror ( errno ) ); +		goto err; +	} +	daemonised = 1; /* Direct messages to syslog now */ + +	/* Open pid file */ +	snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid", +		   interface ); +	fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ), +		    ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ); +	if ( fd < 0 ) { +		logmsg ( LOG_ERR, "Could not open %s for writing: %s\n", +			 pidfile, strerror ( errno ) ); +		goto err; +	} + +	/* Write pid to file */ +	pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() ); +	if ( write ( fd, pid, pidlen ) != pidlen ) { +		logmsg ( LOG_ERR, "Could not write %s: %s\n", +			 pidfile, strerror ( errno ) ); +		goto err; +	} + +	close ( fd ); +	return 0; + + err: +	if ( fd >= 0 ) +		close ( fd ); +	return -1; +} + +int main ( int argc, char **argv ) { +	struct hijack_options options; +	struct hijack_listener listener; +	struct sigaction sa; + +	/* Parse command-line options */ +	if ( parse_options ( argc, argv, &options ) < 0 ) +		exit ( 1 ); + +	/* Set up syslog connection */ +	openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON ); + +	/* Set up listening socket */ +	if ( open_listener ( options.interface, &listener ) < 0 ) +		exit ( 1 ); + +	/* Daemonise on demand */ +	if ( options.daemonise ) { +		if ( daemonise ( options.interface ) < 0 ) +			exit ( 1 ); +	} + +	/* Avoid creating zombies */ +	memset ( &sa, 0, sizeof ( sa ) ); +	sa.sa_handler = SIG_IGN; +	sa.sa_flags = SA_RESTART | SA_NOCLDWAIT; +	if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) { +		logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s", +			 strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* Set 'signalled' flag on SIGINT or SIGHUP */ +	sa.sa_handler = flag_signalled; +	sa.sa_flags = SA_RESTART | SA_RESETHAND; +	if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) { +		logmsg ( LOG_ERR, "Could not set SIGINT handler: %s", +			 strerror ( errno ) ); +		exit ( 1 ); +	} +	if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) { +		logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s", +			 strerror ( errno ) ); +		exit ( 1 ); +	} + +	/* Listen for hijackers */ +	if ( listen_for_hijackers ( &listener, options.interface ) < 0 ) +		exit ( 1 ); + +	close_listener ( &listener ); +	 +	return 0; +} diff --git a/roms/ipxe/src/util/iccfix.c b/roms/ipxe/src/util/iccfix.c new file mode 100644 index 00000000..528bf4b2 --- /dev/null +++ b/roms/ipxe/src/util/iccfix.c @@ -0,0 +1,157 @@ +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <elf.h> +#include <ipxe/tables.h> + +#define DEBUG 0 + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +#define dprintf(...) do {						\ +	if ( DEBUG )							\ +		fprintf ( stderr, __VA_ARGS__ );			\ +	} while ( 0 ) + +#ifdef SELF_INCLUDED + +/** + * Fix up ICC alignments + * + * @v elf		ELF header + * @ret rc		Return status code + * + * See comments in tables.h for an explanation of why this monstrosity + * is necessary. + */ +static int ICCFIX ( void *elf ) { +	ELF_EHDR *ehdr = elf; +	ELF_SHDR *shdr = ( elf + ehdr->e_shoff ); +	size_t shentsize = ehdr->e_shentsize; +	unsigned int shnum = ehdr->e_shnum; +	ELF_SHDR *strtab = ( ( ( void * ) shdr ) + +			     ( ehdr->e_shstrndx * shentsize ) ); +	char *strings = ( elf + strtab->sh_offset ); + +	for ( ; shnum-- ; shdr = ( ( ( void * ) shdr ) + shentsize ) ) { +		char *name = ( strings + shdr->sh_name ); +		unsigned long align = shdr->sh_addralign; +		unsigned long new_align; + +		if ( ( strncmp ( name, ".tbl.", 5 ) == 0 ) && +		     ( align >= ICC_ALIGN_HACK_FACTOR ) ) { +			new_align = ( align / ICC_ALIGN_HACK_FACTOR ); +			shdr->sh_addralign = new_align; +			dprintf ( "Section \"%s\": alignment %ld->%ld\n", +				  name, align, new_align ); +		} +	} +	return 0; +} + +#else /* SELF_INCLUDED */ + +#define SELF_INCLUDED + +/* Include iccfix32() function */ +#define ELF_EHDR Elf32_Ehdr +#define ELF_SHDR Elf32_Shdr +#define ICCFIX iccfix32 +#include "iccfix.c" +#undef ELF_EHDR +#undef ELF_SHDR +#undef ICCFIX + +/* Include iccfix64() function */ +#define ELF_EHDR Elf64_Ehdr +#define ELF_SHDR Elf64_Shdr +#define ICCFIX iccfix64 +#include "iccfix.c" +#undef ELF_EHDR +#undef ELF_SHDR +#undef ICCFIX + +static int iccfix ( const char *filename ) { +	int fd; +	struct stat stat; +	void *elf; +	unsigned char *eident; +	int rc; + +	/* Open and mmap file */ +	fd = open ( filename, O_RDWR ); +	if ( fd < 0 ) { +		eprintf ( "Could not open %s: %s\n", +			  filename, strerror ( errno ) ); +		rc = -1; +		goto err_open; +	} +	if ( fstat ( fd, &stat ) < 0 ) { +		eprintf ( "Could not determine size of %s: %s\n", +			  filename, strerror ( errno ) ); +		rc = -1; +		goto err_fstat; +	} +	elf = mmap ( NULL, stat.st_size, ( PROT_READ | PROT_WRITE ), +		     MAP_SHARED, fd, 0 ); +	if ( elf == MAP_FAILED ) { +		eprintf ( "Could not map %s: %s\n", +			  filename, strerror ( errno ) ); +		rc = -1; +		goto err_mmap; +	} + +	/* Perform fixups */ +	eident = elf; +	switch ( eident[EI_CLASS] ) { +	case ELFCLASS32: +		rc = iccfix32 ( elf ); +		break; +	case ELFCLASS64: +		rc = iccfix64 ( elf ); +		break; +	default: +		eprintf ( "Unknown ELF class %d in %s\n", +			  eident[EI_CLASS], filename ); +		rc = -1; +		break; +	} + +	munmap ( elf, stat.st_size ); + err_mmap: + err_fstat: +	close ( fd ); + err_open: +	return rc; +} + +int main ( int argc, char **argv ) { +	int i; +	int rc; + +	/* Parse command line */ +	if ( argc < 2 ) { +		eprintf ( "Syntax: %s <object_file>...\n", argv[0] ); +		exit ( 1 ); +	} + +	/* Process each object in turn */ +	for ( i = 1 ; i < argc ; i++ ) { +		if ( ( rc = iccfix ( argv[i] ) ) != 0 ) { +			eprintf ( "Could not fix up %s\n", argv[i] ); +			exit ( 1 ); +		} +	} + +	return 0; +} + +#endif /* SELF_INCLUDED */ diff --git a/roms/ipxe/src/util/licence.pl b/roms/ipxe/src/util/licence.pl new file mode 100755 index 00000000..0e43c7b4 --- /dev/null +++ b/roms/ipxe/src/util/licence.pl @@ -0,0 +1,150 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +use strict; +use warnings; +use Getopt::Long; + +# List of licences we can handle +my $known_licences = { +  gpl_any => { +    desc => "GPL (any version)", +    can_subsume => { +      public_domain => 1, +      bsd3 => 1, +      bsd2 => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  gpl2_or_later => { +    desc => "GPL version 2 (or, at your option, any later version)", +    can_subsume => { +      gpl_any => 1, +      public_domain => 1, +      bsd3 => 1, +      bsd2 => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  gpl2_only => { +    desc => "GPL version 2 only", +    can_subsume => { +      gpl_any => 1, +      gpl2_or_later => 1, +      public_domain => 1, +      bsd3 => 1, +      bsd2 => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  public_domain => { +    desc => "Public Domain", +    can_subsume => {}, +  }, +  bsd4 => { +    desc => "BSD Licence (with advertising clause)", +    can_subsume => { +      public_domain => 1, +      bsd3 => 1, +      bsd2 => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  bsd3 => { +    desc => "BSD Licence (without advertising clause)", +    can_subsume => { +      public_domain => 1, +      bsd2 => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  bsd2 => { +    desc => "BSD Licence (without advertising or endorsement clauses)", +    can_subsume => { +      public_domain => 1, +      mit  => 1, +      isc  => 1, +    }, +  }, +  mit => { +    desc => "MIT/X11/Xorg Licence", +    can_subsume => { +      public_domain => 1, +      isc => 1, +    }, +  }, +  isc => { +    desc => "ISC Licence", +    can_subsume => { +      public_domain => 1, +    }, +  }, +}; + +# Parse command-line options +my $verbosity = 1; +Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); +GetOptions ( +  'verbose|v+' => sub { $verbosity++; }, +  'quiet|q+' => sub { $verbosity--; }, +) or die "Could not parse command-line options\n"; + +# Parse licence list from command line +my $licences = {}; +foreach my $licence ( @ARGV ) { +  die "Unknown licence \"$licence\"\n" +      unless exists $known_licences->{$licence}; +  $licences->{$licence} = $known_licences->{$licence}; +} +die "No licences specified\n" unless %$licences; + +# Dump licence list +if ( $verbosity >= 1 ) { +  print "The following licences appear within this file:\n"; +  foreach my $licence ( keys %$licences ) { +    print "  ".$licences->{$licence}->{desc}."\n" +  } +} + +# Apply licence compatibilities to reduce to a single resulting licence +foreach my $licence ( keys %$licences ) { +  # Skip already-deleted licences +  next unless exists $licences->{$licence}; +  # Subsume any subsumable licences +  foreach my $can_subsume ( keys %{$licences->{$licence}->{can_subsume}} ) { +    if ( exists $licences->{$can_subsume} ) { +      print $licences->{$licence}->{desc}." subsumes ". +	  $licences->{$can_subsume}->{desc}."\n" +	  if $verbosity >= 1; +      delete $licences->{$can_subsume}; +    } +  } +} + +# Print resulting licence +die "Cannot reduce to a single resulting licence!\n" +    if ( keys %$licences ) != 1; +( my $licence ) = keys %$licences; +print "The overall licence for this file is:\n  " if $verbosity >= 1; +print $licences->{$licence}->{desc}."\n"; diff --git a/roms/ipxe/src/util/mergerom.pl b/roms/ipxe/src/util/mergerom.pl new file mode 100755 index 00000000..f5c1632b --- /dev/null +++ b/roms/ipxe/src/util/mergerom.pl @@ -0,0 +1,117 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +sub merge_entry_points { +  my $baserom_entry = \shift; +  my $rom_entry = \shift; +  my $offset = shift; + +  if ( $$rom_entry ) { +    my $old_entry = $$baserom_entry; +    $$baserom_entry = ( $offset + $$rom_entry ); +    $$rom_entry = $old_entry; +  } +} + +my @romfiles = @ARGV; +my @roms = map { my $rom = new Option::ROM; $rom->load($_); $rom } @romfiles; + +my $baserom = shift @roms; +my $offset = $baserom->length; + +foreach my $rom ( @roms ) { + +  # Merge initialisation entry point +  merge_entry_points ( $baserom->{init}, $rom->{init}, $offset ); + +  # Merge BOFM header +  merge_entry_points ( $baserom->{bofm_header}, $rom->{bofm_header}, $offset ); + +  # Update PCI header, if present in both +  my $baserom_pci = $baserom->pci_header; +  my $rom_pci = $rom->pci_header; +  if ( $baserom_pci && $rom_pci ) { + +    # Update PCI lengths +    $baserom_pci->{image_length} += $rom_pci->{image_length}; +    if ( exists $baserom_pci->{runtime_length} ) { +      if ( exists $rom_pci->{runtime_length} ) { +	$baserom_pci->{runtime_length} += $rom_pci->{runtime_length}; +      } else { +	$baserom_pci->{runtime_length} += $rom_pci->{image_length}; +      } +    } + +    # Merge CLP entry point +    if ( exists ( $baserom_pci->{clp_entry} ) && +	 exists ( $rom_pci->{clp_entry} ) ) { +      merge_entry_points ( $baserom_pci->{clp_entry}, $rom_pci->{clp_entry}, +			   $offset ); +    } +  } + +  # Update PnP header, if present in both +  my $baserom_pnp = $baserom->pnp_header; +  my $rom_pnp = $rom->pnp_header; +  if ( $baserom_pnp && $rom_pnp ) { +    merge_entry_points ( $baserom_pnp->{bcv}, $rom_pnp->{bcv}, $offset ); +    merge_entry_points ( $baserom_pnp->{bdv}, $rom_pnp->{bdv}, $offset ); +    merge_entry_points ( $baserom_pnp->{bev}, $rom_pnp->{bev}, $offset ); +  } + +  # Update iPXE header, if present +  my $baserom_ipxe = $baserom->ipxe_header; +  my $rom_ipxe = $rom->ipxe_header; +  if ( $baserom_ipxe ) { + +    # Update shrunk length +    $baserom_ipxe->{shrunk_length} = ( $baserom->{length} + +				       ( $rom_ipxe ? +					 $rom_ipxe->{shrunk_length} : +					 $rom->{length} ) ); + +    # Fix checksum +    $baserom_ipxe->fix_checksum(); +  } + +  # Update base length +  $baserom->{length} += $rom->{length}; + +  # Fix checksum for this ROM segment +  $rom->fix_checksum(); + +  # Add this ROM to base ROM +  my $data = substr ( $baserom->get(), 0, $baserom->length() ); +  $data .= $rom->get(); +  $data .= $baserom->next_image()->get() if $baserom->next_image(); +  $baserom->set ( $data ); + +  $offset += $rom->length; +} + +$baserom->pnp_header->fix_checksum() if $baserom->pnp_header; +$baserom->fix_checksum(); +$baserom->save ( "-" ); diff --git a/roms/ipxe/src/util/modrom.pl b/roms/ipxe/src/util/modrom.pl new file mode 100755 index 00000000..cdac0b97 --- /dev/null +++ b/roms/ipxe/src/util/modrom.pl @@ -0,0 +1,226 @@ +#!/usr/bin/perl -w + +use Getopt::Std; + +use constant MINROMSIZE => 8192; +use constant MAXROMSIZE => 262144; + +use constant PCI_PTR_LOC => 0x18;	# from beginning of ROM +use constant PCI_HDR_SIZE => 0x18; +use constant PNP_PTR_LOC => 0x1a;	# from beginning of ROM +use constant PNP_HDR_SIZE => 0x20; +use constant PNP_CHKSUM_OFF => 0x9;	# bytes from beginning of PnP header +use constant PNP_DEVICE_OFF => 0x10;	# bytes from beginning of PnP header +use constant PCI_VEND_ID_OFF => 0x4;	# bytes from beginning of PCI header +use constant PCI_DEV_ID_OFF => 0x6;	# bytes from beginning of PCI header +use constant PCI_SIZE_OFF => 0x10;	# bytes from beginning of PCI header + +use constant UNDI_PTR_LOC => 0x16;	# from beginning of ROM +use constant UNDI_HDR_SIZE => 0x16; +use constant UNDI_CHKSUM_OFF => 0x05; + +use strict; + +use vars qw(%opts); + +use bytes; + +sub getromsize ($) { +	my ($romref) = @_; +	my $i; + +	print STDERR "BIOS extension ROM Image did not start with 0x55 0xAA\n" +		if (substr($$romref, 0, 2) ne "\x55\xaa"); +	my $size = ord(substr($$romref, 2, 1)) * 512; +	for ($i = MINROMSIZE; $i < MAXROMSIZE and $i < $size; $i *= 2) { } +	print STDERR "$size is a strange size for a boot ROM\n" +		if ($size > 0 and $i > $size); +	return ($size); +} + +sub addident ($) { +	my ($romref) = @_; + +	return (0) unless (my $s = $opts{'i'}); +	# include the terminating NUL byte too +	$s .= "\x00"; +	my $len = length($s); +	# Put the identifier in only if the space is blank +	my $pos = length($$romref) - $len - 2; +	return (0) if (substr($$romref, $pos, $len) ne ("\xFF" x $len)); +	substr($$romref, $pos, $len) = $s; +	return ($pos); +} + +sub pcipnpheaders ($$) { +	my ($romref, $identoffset) = @_; +	my ($pci_hdr_offset, $pnp_hdr_offset); + +	$pci_hdr_offset = unpack('v', substr($$romref, PCI_PTR_LOC, 2)); +	$pnp_hdr_offset = unpack('v', substr($$romref, PNP_PTR_LOC, 2)); +	# Sanity checks +	if ($pci_hdr_offset < PCI_PTR_LOC + 2 +		or $pci_hdr_offset > length($$romref) - PCI_HDR_SIZE +		or $pnp_hdr_offset < PNP_PTR_LOC + 2 +		or $pnp_hdr_offset > length($$romref) - PNP_HDR_SIZE +		or substr($$romref, $pci_hdr_offset, 4) ne 'PCIR' +		or substr($$romref, $pnp_hdr_offset, 4) ne '$PnP') { +		$pci_hdr_offset = $pnp_hdr_offset = 0; +	} else { +		printf "PCI header at %#x and PnP header at %#x\n", +			$pci_hdr_offset, $pnp_hdr_offset; +	} +	if ($pci_hdr_offset > 0) { +		my ($pci_vendor_id, $pci_device_id); +		# if no -p option, just report what's there +		if (!defined($opts{'p'})) { +			$pci_vendor_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2)); +			$pci_device_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2)); +			printf "PCI Vendor ID %#x Device ID %#x\n", +				$pci_vendor_id, $pci_device_id; +		} else { +			substr($$romref, $pci_hdr_offset + PCI_SIZE_OFF, 2) +				= pack('v', length($$romref) / 512); +			($pci_vendor_id, $pci_device_id) = split(/,/, $opts{'p'}); +			substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2) +				= pack('v', oct($pci_vendor_id)) if ($pci_vendor_id); +			substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2) +				= pack('v', oct($pci_device_id)) if ($pci_device_id); +		} +	} +	if ($pnp_hdr_offset > 0 and defined($identoffset)) { +		# Point to device id string at end of ROM image +		substr($$romref, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) +			= pack('v', $identoffset); +		substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = "\x00"; +		my $sum = unpack('%8C*', substr($$romref, $pnp_hdr_offset, +			PNP_HDR_SIZE)); +		substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = chr(256 - $sum); +	} +} + +sub undiheaders ($) { +	my ($romref) = @_; +	my ($undi_hdr_offset); + +	$undi_hdr_offset = unpack('v', substr($$romref, UNDI_PTR_LOC, 2)); +	# Sanity checks +	if ($undi_hdr_offset < UNDI_PTR_LOC + 2 +		or $undi_hdr_offset > length($$romref) - UNDI_HDR_SIZE +		or substr($$romref, $undi_hdr_offset, 4) ne 'UNDI') { +		$undi_hdr_offset = 0; +	} else { +		printf "UNDI header at %#x\n", $undi_hdr_offset; +	} +	if ($undi_hdr_offset > 0) { +		substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = "\x00"; +		my $sum = unpack('%8C*', substr($$romref, $undi_hdr_offset, +			UNDI_HDR_SIZE)); +		substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = chr(256 - $sum); +	} +} + +sub writerom ($$) { +	my ($filename, $romref) = @_; + +	open(R, ">$filename") or die "$filename: $!\n"; +	print R $$romref; +	close(R); +} + +sub checksum ($) { +	my ($romref) = @_; + +	substr($$romref, 6, 1) = "\x00"; +	my $sum = unpack('%8C*', $$romref); +	substr($$romref, 6, 1) = chr(256 - $sum); +	# Double check +	$sum = unpack('%8C*', $$romref); +	if ($sum != 0) { +		print "Checksum fails\n" +	} elsif ($opts{'v'}) { +		print "Checksum ok\n"; +	} +} + +sub makerom () { +	my ($rom, $romsize); + +	getopts('3xi:p:s:v', \%opts); +	$ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n"; +	open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; +	# Read in the whole ROM in one gulp +	my $filesize = read(R, $rom, MAXROMSIZE+1); +	close(R); +	defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; +	print "$filesize bytes read\n" if $opts{'v'}; +	# If PXE image, just fill the length field and write it out +	if ($opts{'x'}) { +		substr($rom, 2, 1) = chr((length($rom) + 511) / 512); +		&writerom($ARGV[0], \$rom); +		return; +	} +	# Size specified with -s overrides value in 3rd byte in image +	# -s 0 means round up to next 512 byte block +	if (defined($opts{'s'})) { +		if (($romsize = oct($opts{'s'})) <= 0) { +			# NB: This roundup trick only works on powers of 2 +			$romsize = ($filesize + 511) & ~511 +		} +	} else { +		$romsize = &getromsize(\$rom); +		# 0 put there by *loader.S means makerom should pick the size +		if ($romsize == 0) { +			# Shrink romsize down to the smallest power of two that will do +			for ($romsize = MAXROMSIZE; +				$romsize > MINROMSIZE and $romsize >= 2*$filesize; +				$romsize /= 2) { } +		} +	} +	if ($filesize > $romsize) { +		print STDERR "ROM size of $romsize not big enough for data, "; +		# NB: This roundup trick only works on powers of 2 +		$romsize = ($filesize + 511) & ~511; +		print "will use $romsize instead\n" +	} +	# Pad with 0xFF to $romsize +	$rom .= "\xFF" x ($romsize - length($rom)); +	if ($romsize >= 128 * 1024) { +		print "Warning: ROM size exceeds extension BIOS limit\n"; +	} +	substr($rom, 2, 1) = chr(($romsize / 512) % 256); +	print "ROM size is $romsize\n" if $opts{'v'}; +	my $identoffset = &addident(\$rom); +	&pcipnpheaders(\$rom, $identoffset); +	&undiheaders(\$rom); +	# 3c503 requires last two bytes to be 0x80 +	substr($rom, MINROMSIZE-2, 2) = "\x80\x80" +		if ($opts{'3'} and $romsize == MINROMSIZE); +	&checksum(\$rom); +	&writerom($ARGV[0], \$rom); +} + +sub modrom () { +	my ($rom); + +	getopts('p:v', \%opts); +	$ARGV[0] or die "Usage: $0 [-p vendorid,deviceid] rom-file\n"; +	open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; +	# Read in the whole ROM in one gulp +	my $filesize = read(R, $rom, MAXROMSIZE+1); +	close(R); +	defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; +	print "$filesize bytes read\n" if $opts{'v'}; +	&pcipnpheaders(\$rom); +	&undiheaders(\$rom); +	&checksum(\$rom); +	&writerom($ARGV[0], \$rom); +} + +# Main routine. See how we were called and behave accordingly +if ($0 =~ m:modrom(\.pl)?$:) { +	&modrom(); +} else { +	&makerom(); +} +exit(0); diff --git a/roms/ipxe/src/util/mucurses_test.c b/roms/ipxe/src/util/mucurses_test.c new file mode 100644 index 00000000..586562df --- /dev/null +++ b/roms/ipxe/src/util/mucurses_test.c @@ -0,0 +1,63 @@ +#include "../include/curses.h" +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +void get_iscsi_chap_secret( char * ); +void mdelay( int msecs ); + +int main ( void ) { +	char secret[16]; +	initscr(); +	echo(); +	werase(stdscr); +	box( stdscr, '|', '-' ); +	get_iscsi_chap_secret(secret); + +	mvwprintw( stdscr, 3, 5, "password is \"%s\"", secret ); +	mdelay(2500); + +	stdscr->scr->exit(stdscr->scr); + +	return 0; +} + +void get_iscsi_chap_secret( char *sec ) { +	char 	*title = "Set new iSCSI CHAP secret", +		*msg = "Configure the iSCSI access secret", +		pw1[17], pw2[17]; +	WINDOW *secret; + +	secret = newwin( stdscr->height / 2, +			 stdscr->width / 2, +			 stdscr->height / 4, +			 stdscr->width / 4 ); + +	wborder( secret, '|', '|', '-', '-', '+', '+', '+', '+' ); +	mvwprintw( secret, 1, 2, "%s", title ); +	mvwhline( secret, 2, 1, '-' | secret->attrs, secret->width - 2 ); +	mvwprintw( secret, 4, 2, "%s", msg ); +	mvwprintw( secret, 6, 3, "secret" ); +	mvwprintw( secret, 8, 3, "confirm" ); + start: +	mvwhline( secret, 6, 12, '_' | secret->attrs, 16 ); +	mvwhline( secret, 8, 12, '_' | secret->attrs, 16 ); + +	wmove( secret, 6, 12 ); +	wgetnstr( secret, pw1, 16 ); +	wmove( secret, 8, 12 ); +	wgetnstr( secret, pw2, 16 ); + +	if ( strcmp( pw1, pw2 ) == 0 ) { +		strcpy( sec, pw1 ); +		werase( secret ); +	} +	else { +		mvwprintw( secret, 10, 3, "Passwords do not match" ); +		goto start; +	} +} + +void mdelay ( int msecs ) { +	usleep( msecs * 1000 ); +} diff --git a/roms/ipxe/src/util/niclist.pl b/roms/ipxe/src/util/niclist.pl new file mode 100755 index 00000000..0600c823 --- /dev/null +++ b/roms/ipxe/src/util/niclist.pl @@ -0,0 +1,588 @@ +#!/usr/bin/env perl +# +# Generates list of supported NICs with PCI vendor/device IDs, driver name +# and other useful things. +# +# Initial version by Robin Smidsrød <robin@smidsrod.no> +# + +use strict; +use warnings; +use autodie; +use v5.10; + +use File::stat; +use File::Basename qw(basename); +use File::Find (); +use Getopt::Long qw(GetOptions); + +GetOptions( +    'help'       => \( my $help     = 0      ), +    'format=s'   => \( my $format   = 'text' ), +    'sort=s'     => \( my $sort     = 'bus,ipxe_driver,ipxe_name' ), +    'columns=s'  => \( my $columns  = 'bus,vendor_id,device_id,' +                                    . 'vendor_name,device_name,ipxe_driver,' +                                    . 'ipxe_name,ipxe_description,file,legacy_api' +                     ), +    'pci-url=s'  => \( my $pci_url  = 'http://pciids.sourceforge.net/v2.2/pci.ids' ), +    'pci-file=s' => \( my $pci_file = '/tmp/pci.ids' ), +    'output=s'   => \( my $output   = '' ), +); + +die(<<"EOM") if $help; +Usage: $0 [options] [<directory>] + +Options: +    --help     This page +    --format   Set output format +    --sort     Set output sort order (comma-separated) +    --columns  Set output columns (comma-separated) +    --pci-url  URL to pci.ids file +    --pci-file Cache file for downloaded pci.ids +    --output   Output file (not specified is STDOUT) + +Output formats: +    text, csv, json, html, dokuwiki + +Column names (default order): +    bus, vendor_id, device_id, vendor_name, device_name, +    ipxe_driver, ipxe_name, ipxe_description, file, legacy_api +EOM + +# Only load runtime requirements if actually in use +given($format) { +    when( /csv/  ) { +                       eval { require Text::CSV; }; +                       die("Please install Text::CSV CPAN module to use this feature.\n") +                           if $@; +                   } +    when( /json/ ) { +                       eval { require JSON; }; +                       die("Please install JSON CPAN module to use this feature.\n") +                           if $@; +                   } +    when( /html/ ) { +                       eval { require HTML::Entities; }; +                       die("Please install HTML::Entities CPAN module to use this feature.\n") +                           if $@; +                   } +    default        { } +} + +# Scan source dir and build NIC list +my $ipxe_src_dir = shift || '.'; # Default to current directory +my $ipxe_nic_list = build_ipxe_nic_list( $ipxe_src_dir ); + +# Download pci.ids file and parse it +fetch_pci_ids_file($pci_url, $pci_file); +my $pci_id_map = build_pci_id_map($pci_file); + +# Merge 'official' vendor/device names and sort list +update_ipxe_nic_names($ipxe_nic_list, $pci_id_map); +my $sorted_list = sort_ipxe_nic_list($ipxe_nic_list, $sort); + +# Run specified formatter +my $column_names = parse_columns_param($columns); +say STDERR "Formatting NIC list in format '$format' with columns: " +         . join(", ", @$column_names); +my $formatter = \&{ "format_nic_list_$format" }; +my $report = $formatter->( $sorted_list, $column_names ); + +# Print final report +if ( $output and $output ne '-' ) { +    say STDERR "Printing report to '$output'..."; +    open( my $out_fh, ">", $output ); +    print $out_fh $report; +    close($out_fh); +} +else { +    print STDOUT $report; +} + +exit; + +# fetch URL into specified filename +sub fetch_pci_ids_file { +    my ($url, $filename) = @_; +    my @cmd = ( "wget", "--quiet", "-O", $filename, $url ); +    my @touch = ( "touch", $filename ); +    if ( -r $filename ) { +        my $age = time - stat($filename)->mtime; +        # Refresh if older than 1 day +        if ( $age > 86400 ) { +            say STDERR "Refreshing $filename from $url..."; +            system(@cmd); +            system(@touch); +        } +    } +    else { +        say STDERR "Fetching $url into $filename..."; +        system(@cmd); +        system(@touch); +    } +    return $filename; +} + +sub build_pci_id_map { +    my ($filename) = @_; +    say STDERR "Building PCI ID map..."; + +    my $devices = {}; +    my $classes = {}; +    my $pci_id = qr/[[:xdigit:]]{4}/; +    my $c_id = qr/[[:xdigit:]]{2}/; +    my $non_space = qr/[^\s]/; + +    # open pci.ids file specified +    open( my $fh, "<", $filename ); + +    # For devices +    my $vendor_id = ""; +    my $vendor_name = ""; +    my $device_id = ""; +    my $device_name = ""; + +    # For classes +    my $class_id = ""; +    my $class_name = ""; +    my $subclass_id = ""; +    my $subclass_name = ""; + +    while(<$fh>) { +        # skip # and blank lines +        next if m/^$/; +        next if m/^\s*#/; + +        # Vendors, devices and subsystems. Please keep sorted. +        # Syntax: +        # vendor  vendor_name +        #   device  device_name             <-- single tab +        #       subvendor subdevice  subsystem_name <-- two tabs +        if ( m/^ ($pci_id) \s+ ( $non_space .* ) /x ) { +            $vendor_id = lc $1; +            $vendor_name = $2; +            $devices->{$vendor_id} = { name => $vendor_name }; +            next; +        } + +        if ( $vendor_id and m/^ \t ($pci_id) \s+ ( $non_space .* ) /x ) { +            $device_id = lc $1; +            $device_name = $2; +            $devices->{$vendor_id}->{'devices'} //= {}; +            $devices->{$vendor_id}->{'devices'}->{$device_id} = { name => $device_name }; +            next; +        } + +        if ( $vendor_id and $device_id and m/^ \t{2} ($pci_id) \s+ ($pci_id) \s+ ( $non_space .* ) /x ) { +            my $subvendor_id = lc $1; +            my $subdevice_id = lc $2; +            my $subsystem_name = $3; +            $devices->{$vendor_id}->{'devices'}->{$device_id}->{'subvendor'} //= {}; +            $devices->{$vendor_id}->{'devices'}->{$device_id}->{'subvendor'}->{$subvendor_id} //= {}; +            $devices->{$vendor_id}->{'devices'}->{$device_id}->{'subvendor'}->{$subvendor_id}->{'devices'} //= {}; +            $devices->{$vendor_id}->{'devices'}->{$device_id}->{'subvendor'}->{$subvendor_id}->{'devices'}->{$subdevice_id} = { name => $subsystem_name }; +            next; +        } + +        # List of known device classes, subclasses and programming interfaces +        # Syntax: +        # C class   class_name +        #   subclass    subclass_name       <-- single tab +        #       prog-if  prog-if_name   <-- two tabs +        if ( m/^C \s+ ($c_id) \s+ ( $non_space .* ) /x ) { +            $class_id = lc $1; +            $class_name = $2; +            $classes->{$class_id} = { name => $class_name }; +            next; +        } + +        if ( $class_id and m/^ \t ($c_id) \s+ ( $non_space .* ) /x ) { +            $subclass_id = lc $1; +            $subclass_name = $2; +            $classes->{$class_id}->{'subclasses'} //= {}; +            $classes->{$class_id}->{'subclasses'}->{$subclass_id} = { name => $subclass_name }; +            next; +        } + +        if ( $class_id and $subclass_id and m/^ \t{2} ($c_id) \s+ ( $non_space .* )  /x ) { +            my $prog_if_id = lc $1; +            my $prog_if_name = $2; +            $classes->{$class_id}->{'subclasses'}->{$subclass_id}->{'programming_interfaces'} //= {}; +            $classes->{$class_id}->{'subclasses'}->{$subclass_id}->{'programming_interfaces'}->{$prog_if_id} = { name => $prog_if_name }; +            next; +        } +    } + +    close($fh); + +    # Populate subvendor names +    foreach my $vendor_id ( keys %$devices ) { +        my $device_map = $devices->{$vendor_id}->{'devices'}; +        foreach my $device_id ( keys %$device_map ) { +            my $subvendor_map = $device_map->{$device_id}->{'subvendor'}; +            foreach my $subvendor_id ( keys %$subvendor_map ) { +                $subvendor_map->{$subvendor_id}->{'name'} = $devices->{$subvendor_id}->{'name'} || ""; +            } +        } +    } + +    return { +        'devices' => $devices, +        'classes' => $classes, +    }; +} + +# Scan through C code and parse ISA_ROM and PCI_ROM lines +sub build_ipxe_nic_list { +    my ($dir) = @_; +    say STDERR "Building iPXE NIC list from " . ( $dir eq '.' ? 'current directory' : $dir ) . "..."; + +    # recursively iterate through dir and find .c files +    my @c_files; +    File::Find::find(sub { +        # only process files +        return if -d $_; +        # skip unreadable files +        return unless -r $_; +        # skip all but files with .c extension +        return unless /\.c$/; +        push @c_files, $File::Find::name; +    }, $dir); + +    # Look for ISA_ROM or PCI_ROM lines +    my $ipxe_nic_list = []; +    my $hex_id = qr/0 x [[:xdigit:]]{4} /x; +    my $quote = qr/ ['"] /x; +    my $non_space = qr/ [^\s] /x; +    my $rom_line_counter = 0; +    foreach my $c_path ( sort @c_files ) { +        my $legacy = 0; +        open( my $fh, "<", $c_path ); +        my $c_file = $c_path; +        $c_file =~ s{^\Q$dir\E/?}{} if -d $dir; # Strip directory from reported filename +        my $ipxe_driver = basename($c_file, '.c'); +        while(<$fh>) { +            # Most likely EtherBoot legacy API +            $legacy = 1 if m/struct \s* nic \s*/x; + +            # parse ISA|PCI_ROM lines into hashref and append to $ipxe_nic_list +            next unless m/^ \s* (?:ISA|PCI)_ROM /x; +            $rom_line_counter++; +            chomp; +            #say; # for debugging regexp +            if ( m/^ \s* ISA_ROM \s* \( \s* $quote ( .*? ) $quote \s* , \s* $quote ( .*? ) $quote \s* \) /x ) { +                my $image = $1; +                my $name = $2; +                push @$ipxe_nic_list, { +                    file             => $c_file, +                    bus              => 'isa', +                    ipxe_driver      => $ipxe_driver, +                    ipxe_name        => $image, +                    ipxe_description => $name, +                    legacy_api       => ( $legacy ? 'yes' : 'no' ), +                }; +                next; +            } +            if ( m/^ \s* PCI_ROM \s* \( \s* ($hex_id) \s* , \s* ($hex_id) \s* , \s* $quote (.*?) $quote \s* , \s* $quote (.*?) $quote /x ) { +                my $vendor_id = lc $1; +                my $device_id = lc $2; +                my $name = $3; +                my $desc = $4; +                push @$ipxe_nic_list, { +                    file             => $c_file, +                    bus              => 'pci', +                    vendor_id        => substr($vendor_id, 2), # strip 0x +                    device_id        => substr($device_id, 2), # strip 0x +                    ipxe_driver      => $ipxe_driver, +                    ipxe_name        => $name, +                    ipxe_description => $desc, +                    legacy_api       => ( $legacy ? 'yes' : 'no' ), +                }; +                next; +            } +        } +        close($fh); +    } + +    # Verify all ROM lines where parsed properly +    my @isa_roms = grep { $_->{'bus'} eq 'isa' } @$ipxe_nic_list; +    my @pci_roms = grep { $_->{'bus'} eq 'pci' } @$ipxe_nic_list; +    if ( $rom_line_counter != ( @isa_roms + @pci_roms ) ) { +        say STDERR "Found ROM lines: $rom_line_counter"; +        say STDERR "Extracted ISA_ROM lines: " . scalar @isa_roms; +        say STDERR "Extracted PCI_ROM lines: " . scalar @pci_roms; +        die("Mismatch between number of ISA_ROM/PCI_ROM lines and extracted entries. Verify regular expressions.\n"); +    } + +    return $ipxe_nic_list; +} + +# merge vendor/product name from $pci_id_map into $ipxe_nic_list +sub update_ipxe_nic_names { +    my ($ipxe_nic_list, $pci_id_map) = @_; +    say STDERR "Merging 'official' vendor/device names..."; + +    foreach my $nic ( @$ipxe_nic_list ) { +        next unless $nic->{'bus'} eq 'pci'; +        $nic->{'vendor_name'} = $pci_id_map->{'devices'}->{ $nic->{'vendor_id'} }->{'name'} || ""; +        $nic->{'device_name'} = $pci_id_map->{'devices'}->{ $nic->{'vendor_id'} }->{'devices'}->{ $nic->{'device_id'} }->{'name'} || ""; +    } +    return $ipxe_nic_list; # Redundant, as we're mutating the input list, useful for chaining calls +} + +# Sort entries in NIC list according to sort criteria +sub sort_ipxe_nic_list { +    my ($ipxe_nic_list, $sort_column_names) = @_; +    my @sort_column_names = @{ parse_columns_param($sort_column_names) }; +    say STDERR "Sorting NIC list by: " . join(", ", @sort_column_names ); +    # Start at the end of the list and resort until list is exhausted +    my @sorted_list = @{ $ipxe_nic_list }; +    while(@sort_column_names) { +        my $column_name = pop @sort_column_names; +        @sorted_list = sort { ( $a->{$column_name} || "" ) cmp ( $b->{$column_name} || "" ) } +                       @sorted_list; +    } +    return \@sorted_list; +} + +# Parse comma-separated values into array +sub parse_columns_param { +    my ($columns) = @_; +    return [ +        grep { is_valid_column($_) } # only include valid entries +        map  { s/\s//g; $_; }        # filter whitespace +        split( /,/, $columns )       # split on comma +    ]; +} + +# Return true if the input column name is valid +sub is_valid_column { +    my ($name) = @_; +    my $valid_column_map = { +        map { $_ => 1 } +        qw( +           bus file legacy_api +           ipxe_driver ipxe_name ipxe_description +           vendor_id device_id vendor_name device_name +        ) +    }; +    return unless $name; +    return unless $valid_column_map->{$name}; +    return 1; +} + +# Output NIC list in plain text +sub format_nic_list_text { +    my ($nic_list, $column_names) = @_; +    return join("\n", +        map { format_nic_text($_, $column_names) } +        @$nic_list +    ); +} + +# Format one ipxe_nic_list entry for display +# Column order not supported by text format +sub format_nic_text { +    my ($nic, $column_names) = @_; +    my $labels = { +        bus              => 'Bus:             ', +        ipxe_driver      => 'iPXE driver:     ', +        ipxe_name        => 'iPXE name:       ', +        ipxe_description => 'iPXE description:', +        file             => 'Source file:     ', +        legacy_api       => 'Using legacy API:', +        vendor_id        => 'PCI vendor ID:   ', +        device_id        => 'PCI device ID:   ', +        vendor_name      => 'Vendor name:     ', +        device_name      => 'Device name:     ', +    }; +    my $pci_only = { +        vendor_id   => 1, +        device_id   => 1, +        vendor_name => 1, +        device_name => 1, +    }; +    my $output = ""; +    foreach my $column ( @$column_names ) { +        next if $nic->{'bus'} eq 'isa' and $pci_only->{$column}; +        $output .= $labels->{$column} +                .  " " +                . ( $nic->{$column} || "" ) +                . "\n"; +    } +    return $output; +} + +# Output NIC list in JSON +sub format_nic_list_json { +    my ($nic_list, $column_names) = @_; + +    # Filter columns not mentioned +    my @nics; +    foreach my $nic ( @$nic_list ) { +        my $filtered_nic = {}; +        foreach my $key ( @$column_names ) { +            $filtered_nic->{$key} = $nic->{$key}; +        } +        push @nics, $filtered_nic; +    } + +    return JSON->new->pretty->utf8->encode(\@nics); +} + +# Output NIC list in CSV +sub format_nic_list_csv { +    my ($nic_list, $column_names) = @_; +    my @output; + +    # Output CSV header +    my $csv = Text::CSV->new(); +    if ( $csv->combine( @$column_names ) ) { +        push @output, $csv->string(); +    } + +    # Output CSV lines +    foreach my $nic ( @$nic_list ) { +        my @columns = @{ $nic }{ @$column_names }; +        if ( $csv->combine( @columns ) ) { +            push @output, $csv->string(); +        } +    } +    return join("\n", @output) . "\n"; +} + +# Output NIC list in HTML +sub format_nic_list_html { +    my ($nic_list, $column_names) = @_; +    my @output; + +    push @output, <<'EOM'; +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Network cards supported by iPXE</title> +<style> +table.tablesorter { + border: thin solid black; +} + +table.tablesorter thead { + background-color: #EEE; +} + +table.tablesorter thead th { + font-weight: bold; +} + +table.tablesorter tbody td { + vertical-align: top; + padding-left: 0.25em; + padding-right: 0.25em; + padding-bottom: 0.125em; + white-space: nowrap; +} + +table.tablesorter tbody tr.even { + background-color: #eee; +} + +table.tablesorter tbody tr.odd { + background-color: #fff; +} +</style> +</head> +<body> +<h1>Network cards supported by iPXE</h1> +<table class="tablesorter"> +<thead> +EOM + +    # Output HTML header +    push @output, "<tr>" +                . join("", +                    map { "<th>" . HTML::Entities::encode($_) . "</th>" } +                    @$column_names +                  ) +                . "</tr>"; + +    push @output, <<"EOM"; +</thead> +<tbody> +EOM +    # Output HTML lines +    my $counter = 0; +    foreach my $nic ( @$nic_list ) { +        my @columns = @{ $nic }{ @$column_names }; # array slice from hashref, see perldoc perldata if confusing +        push @output, q!<tr class="! . ( $counter % 2 ? 'even' : 'odd' ) . q!">! +                    . join("", +                        map { "<td>" . HTML::Entities::encode( $_ || "" ) . "</td>" } +                        @columns +                      ) +                    . "</tr>"; +        $counter++; +    } + +    push @output, <<'EOM'; +</tbody> +</table> +<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> +<script> +/* + * + * TableSorter 2.0 - Client-side table sorting with ease! + * Version 2.0.5b + * @requires jQuery v1.2.3 + * From http://tablesorter.com/ + * + * Copyright (c) 2007 Christian Bach + * Examples and docs at: http://tablesorter.com + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +(function($){$.extend({tablesorter:new +function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1 +var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); +</script> +<script type="text/javascript"> +$(document).ready(function() { +    $("table.tablesorter").tablesorter(); +}); +</script> +</body> +</html> +EOM +    return join("\n", @output); +} + +# Output NIC list in DokuWiki format (for http://ipxe.org) +sub format_nic_list_dokuwiki { +    my ($nic_list, $column_names) = @_; +    my @output; + +    push @output, <<'EOM'; +EOM + +    # Output DokuWiki table header +    push @output, "^" +                . join("^", +                    map { $_ || "" } +                    @$column_names +                  ) +                . "^"; + +    # Output DokuWiki table entries +    foreach my $nic ( @$nic_list ) { +        my @columns = @{ $nic }{ @$column_names }; # array slice from hashref, see perldoc perldata if confusing +        push @output, '|' +                    . join('|', +                        map { $_ || "" } +                        @columns +                      ) +                    . '|'; +    } + +    return join("\n", @output); +} diff --git a/roms/ipxe/src/util/nrv2b.c b/roms/ipxe/src/util/nrv2b.c new file mode 100644 index 00000000..031f5d9c --- /dev/null +++ b/roms/ipxe/src/util/nrv2b.c @@ -0,0 +1,1500 @@ +/************************************************************** +    Form adapted from lzhuf.c +    written by Haruyasu Yoshizaki 11/20/1988 +    some minor changes 4/6/1989 +    comments translated by Haruhiko Okumura 4/7/1989 + +    minor beautifications and adjustments for compiling under Linux +    by Markus Gutschke <gutschk@math.uni-muenster.de> +    						1997-01-27 + +    Modifications to allow use as a filter by Ken Yap +    <ken_yap@users.sourceforge.net>. + +						1997-07-01 + +    Small mod to cope with running on big-endian machines +    by Jim Hague <jim.hague@acm.org) +						1998-02-06 + +    Make compression statistics report shorter +    by Ken Yap <ken_yap@users.sourceforge.net>. +						2001-04-25 + +    Replaced algorithm with nrv2b from ucl the compression +    library from upx.  That code is: +    Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer +    And is distributed under the terms of the GPL. +    The conversion was performed  +    by Eric Biederman <ebiederman@lnxi.com>. +                                             20 August 2002 +                                                 +**************************************************************/ +#define UCLPACK_COMPAT 0 +#define NDEBUG 1 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#ifdef __FreeBSD__ +#include <inttypes.h> +#else +#include <stdint.h> +#endif +#include <limits.h> +#include <assert.h> +#if UCLPACK_COMPAT +#include <netinet/in.h> +#endif + +#ifndef VERBOSE +#define Fprintf(x) +#define wterr     0 +#else +#define Fprintf(x) fprintf x +#endif + +#ifndef MAIN +extern +#endif +FILE  *infile, *outfile; + +#if defined(ENCODE) || defined(DECODE) + +#ifndef ENDIAN +#define ENDIAN   0 +#endif +#ifndef BITSIZE +#define BITSIZE 32 +#endif + +static __inline__ void Error(char *message) +{ +	Fprintf((stderr, "\n%s\n", message)); +	exit(EXIT_FAILURE); +} + +/* These will be a complete waste of time on a lo-endian */ +/* system, but it only gets done once so WTF. */ +static unsigned long __attribute__ (( unused )) i86ul_to_host(unsigned long ul) +{ +	unsigned long res = 0; +	int i; +	union +	{ +		unsigned char c[4]; +		unsigned long ul; +	} u; + +	u.ul = ul; +	for (i = 3; i >= 0; i--) +		res = (res << 8) + u.c[i]; +	return res; +} + +static unsigned long host_to_i86ul(unsigned long ul) +{ +	int i; +	union +	{ +		unsigned char c[4]; +		unsigned long ul; +	} u; + +	for (i = 0; i < 4; i++) +	{ +		u.c[i] = ul & 0xff; +		ul >>= 8; +	} +	return u.ul; +} +#endif + + + +#if UCLPACK_COMPAT +/* magic file header for compressed files */ +static const unsigned char magic[8] = +{ 0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a }; + +#endif + +#ifdef ENCODE +/********** NRV2B_99 compression **********/ + +/* Note by limiting the ring buffer I have limited the maximum + * offset to 64K.  Since etherboot rarely gets that big it + * is not a problem and it gives me a firm guarantee + * that I will never get a 3 byte string match that is encodes + * to more than 9/8 it's original size. + * That guaranteee is important to for the inplace decompressor. + * There are better ways to do this if a larger offset and buffer + * would give better compression. + */ +#define N       (65536ul)           /* size of ring buffer */ +#define THRESHOLD       1           /* lower limit for match length */ +#define F            2048           /* upper limit for match length */ +#define M2_MAX_OFFSET                 0xd00 + +/* note: to use default values pass -1, i.e. initialize + * this struct by a memset(x,0xff,sizeof(x)) */ +struct ucl_compress_config +{ +	int bb_endian; +	int bb_size; +	unsigned int max_offset; +	unsigned int max_match; +	int s_level; +	int h_level; +	int p_level; +	int c_flags; +	unsigned int m_size; +}; + +struct ucl_compress +{ +	int init; + +	unsigned int look;          /* bytes in lookahead buffer */ +	 +	unsigned int m_len; +	unsigned int m_off; +	 +	unsigned int last_m_len; +	unsigned int last_m_off; +	 +	const unsigned char *bp; +	const unsigned char *ip; +	const unsigned char *in; +	const unsigned char *in_end; +	unsigned char *out; +	 +	uint64_t bb_b; +	unsigned bb_k; +	unsigned bb_c_endian; +	unsigned bb_c_s; +	unsigned bb_c_s8; +	unsigned char *bb_p; +	unsigned char *bb_op; +	 +	struct ucl_compress_config conf; +	unsigned int *result; + +	unsigned int textsize;      /* text size counter */ +	unsigned int codesize;      /* code size counter */ +	unsigned int printcount; /* counter for reporting progress every 1K +				    bytes */ + +	 +	/* some stats */ +	unsigned long lit_bytes; +	unsigned long match_bytes; +	unsigned long rep_bytes; +	unsigned long lazy; +}; + + + +#define getbyte(c)  ((c).ip < (c).in_end ? *((c).ip)++ : (-1)) + +#define UCL_E_OK               0 +#define UCL_E_INVALID_ARGUMENT 1 +#define UCL_E_OUT_OF_MEMORY    2 +#define UCL_E_ERROR            3 + +/*********************************************************************** +// +************************************************************************/ + +#define SWD_HSIZE	16384 +#define SWD_MAX_CHAIN	2048 +#undef SWD_BEST_OFF + +#define HEAD3(b,p) \ +    (((0x9f5f*(((((uint32_t)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1)) + +#define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8)) +#define NIL2              UINT_MAX + +struct ucl_swd +{ +/* public - "built-in" */ +	unsigned int n; +	unsigned int f; +	unsigned int threshold; +	 +/* public - configuration */ +	unsigned int max_chain; +	unsigned int nice_length; +	int use_best_off; +	unsigned int lazy_insert; +	 +/* public - output */ +	unsigned int m_len; +	unsigned int m_off; +	unsigned int look; +	int b_char; +#if defined(SWD_BEST_OFF) +	unsigned int best_off[ SWD_BEST_OFF ]; +#endif +	 +/* semi public */ +	struct ucl_compress *c; +	unsigned int m_pos; +#if defined(SWD_BEST_OFF) +	unsigned int best_pos[ SWD_BEST_OFF ]; +#endif +	 +/* private */ +	const uint8_t *dict; +	const uint8_t *dict_end; +	unsigned int dict_len; +	 +/* private */ +	unsigned int ip;                /* input pointer (lookahead) */ +	unsigned int bp;                /* buffer pointer */ +	unsigned int rp;                /* remove pointer */ +	unsigned int b_size; +	 +	unsigned char *b_wrap; +	 +	unsigned int node_count; +	unsigned int first_rp; + +	unsigned char b [ N + F + F ]; +	unsigned int head3 [ SWD_HSIZE ]; +	unsigned int succ3 [ N + F ]; +	unsigned int best3 [ N + F ]; +	unsigned int llen3 [ SWD_HSIZE ]; +	unsigned int head2 [ 65536U ]; +}; + +#define s_head3(s,key)        s->head3[key] + + +#if !defined( NDEBUG) +static void assert_match(const struct ucl_swd * swd, unsigned int m_len, +	unsigned int m_off ) + +{ +	const struct ucl_compress *c = swd->c; +	unsigned int d_off; +	 +	assert(m_len >= 2); +	if (m_off <= (unsigned int) (c->bp - c->in)) +	{ +		assert(c->bp - m_off + m_len < c->ip); +		assert(memcmp(c->bp, c->bp - m_off, m_len) == 0); +	} +	else +	{ +		assert(swd->dict != NULL); +		d_off = m_off - (unsigned int) (c->bp - c->in); +		assert(d_off <= swd->dict_len); +		if (m_len > d_off) +		{ +			assert(memcmp(c->bp, swd->dict_end - d_off, d_off) == +				0); + +			assert(c->in + m_len - d_off < c->ip); +			assert(memcmp(c->bp + d_off, c->in, m_len - d_off) == +				0); + +		} +		else +		{ +			assert(memcmp(c->bp, swd->dict_end - d_off, m_len) == +				0); + +		} +	} +} +#else +#  define assert_match(a,b,c)   ((void)0) +#endif + +/*********************************************************************** +// +************************************************************************/ + + +static +void swd_initdict(struct ucl_swd *s, const uint8_t *dict, unsigned int dict_len) + +{ +	s->dict = s->dict_end = NULL; +	s->dict_len = 0; + +	if (!dict || dict_len <= 0) +		return; +	if (dict_len > s->n) +	{ +		dict += dict_len - s->n; +		dict_len = s->n; +	} + +	s->dict = dict; +	s->dict_len = dict_len; +	s->dict_end = dict + dict_len; +	memcpy(s->b,dict,dict_len); +	s->ip = dict_len; +} + + +static +void swd_insertdict(struct ucl_swd *s, unsigned int node, unsigned int len) +{ +	unsigned int key; + +	s->node_count = s->n - len; +	s->first_rp = node; + +	while (len-- > 0) +	{ +		key = HEAD3(s->b,node); +		s->succ3[node] = s_head3(s,key); +		s->head3[key] = (unsigned int)(node); +		s->best3[node] = (unsigned int)(s->f + 1); +		s->llen3[key]++; +		assert(s->llen3[key] <= s->n); + +		key = HEAD2(s->b,node); +		s->head2[key] = (unsigned int)(node); + +		node++; +	} +} + +/*********************************************************************** +// +************************************************************************/ + + +static +int swd_init(struct ucl_swd *s, const uint8_t *dict, unsigned int dict_len) +{ +	unsigned int i = 0; + +	if (s->n == 0) +		s->n = N; +	if (s->f == 0) +		s->f = F; +	s->threshold = THRESHOLD; +	if (s->n > N || s->f > F) +		return UCL_E_INVALID_ARGUMENT; + +	/* defaults */ +	s->max_chain = SWD_MAX_CHAIN; +	s->nice_length = s->f; +	s->use_best_off = 0; +	s->lazy_insert = 0; + +	s->b_size = s->n + s->f; +	if (s->b_size + s->f >= UINT_MAX) +		return UCL_E_ERROR; +	s->b_wrap = s->b + s->b_size; +	s->node_count = s->n; + +	memset(s->llen3, 0, sizeof(s->llen3[0]) * SWD_HSIZE); +	for (i = 0; i < 65536U; i++) +		s->head2[i] = NIL2; + +	s->ip = 0; +	swd_initdict(s,dict,dict_len); +	s->bp = s->ip; +	s->first_rp = s->ip; + +	assert(s->ip + s->f <= s->b_size); + +	s->look = (unsigned int) (s->c->in_end - s->c->ip); +	if (s->look > 0) +	{ +		if (s->look > s->f) +			s->look = s->f; +		memcpy(&s->b[s->ip],s->c->ip,s->look); +		s->c->ip += s->look; +		s->ip += s->look; +	} +	if (s->ip == s->b_size) +		s->ip = 0; + +	if (s->look >= 2 && s->dict_len > 0) +		swd_insertdict(s,0,s->dict_len); + +	s->rp = s->first_rp; +	if (s->rp >= s->node_count) +		s->rp -= s->node_count; +	else +		s->rp += s->b_size - s->node_count; + +	/* unused i */ +	/* unused c */ +	return UCL_E_OK; +} + + +static +void swd_exit(struct ucl_swd *s) +{ +	/* unused s */ +	( void ) s; +} + +#define swd_pos2off(s,pos) \ +	(s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp)) + +/*********************************************************************** +// +************************************************************************/ + +static __inline__ +void swd_getbyte(struct ucl_swd *s) +{ +	int c; + +	if ((c = getbyte(*(s->c))) < 0) +	{ +		if (s->look > 0) +			--s->look; +	} +	else +	{ +		s->b[s->ip] = (uint8_t)(c); +		if (s->ip < s->f) +			s->b_wrap[s->ip] = (uint8_t)(c); +	} +	if (++s->ip == s->b_size) +		s->ip = 0; +	if (++s->bp == s->b_size) +		s->bp = 0; +	if (++s->rp == s->b_size) +		s->rp = 0; +} +/*********************************************************************** +// remove node from lists +************************************************************************/ + +static __inline__ +void swd_remove_node(struct ucl_swd *s, unsigned int node) +{ +	if (s->node_count == 0) +	{ +		unsigned int key; +		 +#ifdef UCL_DEBUG +		if (s->first_rp != UINT_MAX) +		{ +			if (node != s->first_rp) +				printf("Remove %5d: %5d %5d %5d %5d %6d %6d\n", + +					node, s->rp, s->ip, s->bp, s->first_rp, +					s->ip - node, s->ip - s->bp); +			assert(node == s->first_rp); +			s->first_rp = UINT_MAX; +		} +#endif +		 +		key = HEAD3(s->b,node); +		assert(s->llen3[key] > 0); +		--s->llen3[key]; +		 +		key = HEAD2(s->b,node); +		assert(s->head2[key] != NIL2); +		if ((unsigned int) s->head2[key] == node) +			s->head2[key] = NIL2; +	} +	else +		--s->node_count; +} + + +/*********************************************************************** +// +************************************************************************/ + + +static +void swd_accept(struct ucl_swd *s, unsigned int n) +{ +	assert(n <= s->look); + +	if (n > 0) do +	{ +		unsigned int key; + +		swd_remove_node(s,s->rp); + +		/* add bp into HEAD3 */ +		key = HEAD3(s->b,s->bp); +		s->succ3[s->bp] = s_head3(s,key); +		s->head3[key] = (unsigned int)(s->bp); +		s->best3[s->bp] = (unsigned int)(s->f + 1); +		s->llen3[key]++; +		assert(s->llen3[key] <= s->n); + +		/* add bp into HEAD2 */ +		key = HEAD2(s->b,s->bp); +		s->head2[key] = (unsigned int)(s->bp); + +		swd_getbyte(s); +	} while (--n > 0); +} + +/*********************************************************************** +// +************************************************************************/ + +static +void swd_search(struct ucl_swd *s, unsigned int node, unsigned int cnt) +{ +	const unsigned char *p1; +	const unsigned char *p2; +	const unsigned char *px; + +	unsigned int m_len = s->m_len; +	const unsigned char * b  = s->b; +	const unsigned char * bp = s->b + s->bp; +	const unsigned char * bx = s->b + s->bp + s->look; +	unsigned char scan_end1; +	 +	assert(s->m_len > 0); +	 +	scan_end1 = bp[m_len - 1]; +	for ( ; cnt-- > 0; node = s->succ3[node]) +	{ +		p1 = bp; +		p2 = b + node; +		px = bx; +		 +		assert(m_len < s->look); +		 +		if ( +			p2[m_len - 1] == scan_end1 && +			p2[m_len] == p1[m_len] && +			p2[0] == p1[0] && +			p2[1] == p1[1]) +		{ +			unsigned int i; +			assert(memcmp(bp,&b[node],3) == 0); +			 +			p1 += 2; p2 += 2; +			do {} while (++p1 < px && *p1 == *++p2); +			i = p1 - bp; +			 +#ifdef UCL_DEBUG +			if (memcmp(bp,&b[node],i) != 0) +				printf("%5ld %5ld %02x%02x %02x%02x\n", +					(long)s->bp, (long) node, +					bp[0], bp[1], b[node], b[node+1]); +#endif +			assert(memcmp(bp,&b[node],i) == 0); +			 +#if defined(SWD_BEST_OFF) +			if (i < SWD_BEST_OFF) +			{ +				if (s->best_pos[i] == 0) +					s->best_pos[i] = node + 1; +			} +#endif +			if (i > m_len) +			{ +				s->m_len = m_len = i; +				s->m_pos = node; +				if (m_len == s->look) +					return; +				if (m_len >= s->nice_length) +					return; +				if (m_len > (unsigned int) s->best3[node]) +					return; +				scan_end1 = bp[m_len - 1]; +			} +		} +	} +} + +static int swd_search2(struct ucl_swd *s) +{ +	unsigned int key; +	 +	assert(s->look >= 2); +	assert(s->m_len > 0); +	 +	key = s->head2[ HEAD2(s->b,s->bp) ]; +	if (key == NIL2) +		return 0; +#ifdef UCL_DEBUG +	if (memcmp(&s->b[s->bp],&s->b[key],2) != 0) +		printf("%5ld %5ld %02x%02x %02x%02x\n", (long)s->bp, (long)key, +			s->b[s->bp], s->b[s->bp+1], s->b[key], s->b[key+1]); +#endif +	assert(memcmp(&s->b[s->bp],&s->b[key],2) == 0); +#if defined(SWD_BEST_OFF) +	if (s->best_pos[2] == 0) +		s->best_pos[2] = key + 1; +#endif +	 +	if (s->m_len < 2) +	{ +		s->m_len = 2; +		s->m_pos = key; +	} +	return 1; +} + +/*********************************************************************** +// +************************************************************************/ + +static +void swd_findbest(struct ucl_swd *s) +{ +	unsigned int key; +	unsigned int cnt, node; +	unsigned int len; + +	assert(s->m_len > 0); + +	/* get current head, add bp into HEAD3 */ +	key = HEAD3(s->b,s->bp); +	node = s->succ3[s->bp] = s_head3(s,key); +	cnt = s->llen3[key]++; +	assert(s->llen3[key] <= s->n + s->f); +	if (cnt > s->max_chain && s->max_chain > 0) +		cnt = s->max_chain; +	s->head3[key] = (unsigned int)(s->bp); + +	s->b_char = s->b[s->bp]; +	len = s->m_len; +	if (s->m_len >= s->look) +	{ +		if (s->look == 0) +			s->b_char = -1; +		s->m_off = 0; +		s->best3[s->bp] = (unsigned int)(s->f + 1); +	} +	else +	{ +		if (swd_search2(s)) +			if (s->look >= 3) +				swd_search(s,node,cnt); +		if (s->m_len > len) +			s->m_off = swd_pos2off(s,s->m_pos); +		s->best3[s->bp] = (unsigned int)(s->m_len); + +#if defined(SWD_BEST_OFF) +		if (s->use_best_off) +		{ +			int i; +			for (i = 2; i < SWD_BEST_OFF; i++) +				if (s->best_pos[i] > 0) +					s->best_off[i] = +						swd_pos2off(s,s->best_pos[i]-1); + +				else +					s->best_off[i] = 0; +		} +#endif +	} + +	swd_remove_node(s,s->rp); + +	/* add bp into HEAD2 */ +	key = HEAD2(s->b,s->bp); +	s->head2[key] = (unsigned int)(s->bp); +} + + +/*********************************************************************** +// +************************************************************************/ + +static int +init_match ( struct ucl_compress *c, struct ucl_swd *s, +	const uint8_t *dict, unsigned int dict_len, +	uint32_t flags ) +{ +	int r; +	 +	assert(!c->init); +	c->init = 1; +	 +	s->c = c; +	 +	c->last_m_len = c->last_m_off = 0; +	 +	c->textsize = c->codesize = c->printcount = 0; +	c->lit_bytes = c->match_bytes = c->rep_bytes = 0; +	c->lazy = 0; +	 +	r = swd_init(s,dict,dict_len); +	if (r != UCL_E_OK) +	{ +		swd_exit(s); +		return r; +	} +	 +	s->use_best_off = (flags & 1) ? 1 : 0; +	return UCL_E_OK; +} + +static int +find_match ( struct ucl_compress *c, struct ucl_swd *s, +	unsigned int this_len, unsigned int skip ) +{ +	assert(c->init); +	 +	if (skip > 0) +	{ +		assert(this_len >= skip); +		swd_accept(s, this_len - skip); +		c->textsize += this_len - skip + 1; +	} +	else +	{ +		assert(this_len <= 1); +		c->textsize += this_len - skip; +	} +	 +	s->m_len = THRESHOLD; +#ifdef SWD_BEST_OFF +	if (s->use_best_off) +		memset(s->best_pos,0,sizeof(s->best_pos)); +#endif +	swd_findbest(s); +	c->m_len = s->m_len; +	c->m_off = s->m_off; +	 +	swd_getbyte(s); +	 +	if (s->b_char < 0) +	{ +		c->look = 0; +		c->m_len = 0; +		swd_exit(s); +	} +	else +	{ +		c->look = s->look + 1; +	} +	c->bp = c->ip - c->look; +	 +#if 0 +	/* brute force match search */ +	if (c->m_len > THRESHOLD && c->m_len + 1 <= c->look) +	{ +		const uint8_t *ip = c->bp; +		const uint8_t *m  = c->bp - c->m_off; +		const uint8_t *in = c->in; +		 +		if (ip - in > N) +			in = ip - N; +		for (;;) +		{ +			while (*in != *ip) +				in++; +			if (in == ip) +				break; +			if (in != m) +				if (memcmp(in,ip,c->m_len+1) == 0) +					printf("%p %p %p %5d\n",in,ip,m,c->m_len); + +			in++; +		} +	} +#endif +	 +	return UCL_E_OK; +} + + +static int bbConfig(struct ucl_compress *c, int endian, int bitsize) +{ +	if (endian != -1) +	{ +		if (endian != 0) +			return UCL_E_ERROR; +		c->bb_c_endian = endian; +	} +	if (bitsize != -1) +	{ +		if (bitsize != 8 && bitsize != 16 && bitsize != 32 && bitsize != 64) +			return UCL_E_ERROR; +		c->bb_c_s = bitsize; +		c->bb_c_s8 = bitsize / 8; +	} +	c->bb_b = 0; c->bb_k = 0; +	c->bb_p = NULL; +	c->bb_op = NULL; +	return UCL_E_OK; +} + +static void bbWriteBits(struct ucl_compress *c) +{ +	uint8_t *p = c->bb_p; +	uint64_t b = c->bb_b; + +	p[0] = (uint8_t)(b >>  0); +	if (c->bb_c_s >= 16) +	{ +		p[1] = (uint8_t)(b >>  8); +		if (c->bb_c_s >= 32) +		{ +			p[2] = (uint8_t)(b >> 16); +			p[3] = (uint8_t)(b >> 24); +			if (c->bb_c_s == 64) +			{ +				p[4] = (uint8_t)(b >> 32); +				p[5] = (uint8_t)(b >> 40); +				p[6] = (uint8_t)(b >> 48); +				p[7] = (uint8_t)(b >> 56); +			} +		} +	} +} + + +static void bbPutBit(struct ucl_compress *c, unsigned bit) +{ +	assert(bit == 0 || bit == 1); +	assert(c->bb_k <= c->bb_c_s); + +	if (c->bb_k < c->bb_c_s) +	{ +		if (c->bb_k == 0) +		{ +			assert(c->bb_p == NULL); +			c->bb_p = c->bb_op; +			c->bb_op += c->bb_c_s8; +		} +		assert(c->bb_p != NULL); +		assert(c->bb_p + c->bb_c_s8 <= c->bb_op); + +		c->bb_b = (c->bb_b << 1) + bit; +		c->bb_k++; +	} +	else +	{ +		assert(c->bb_p != NULL); +		assert(c->bb_p + c->bb_c_s8 <= c->bb_op); + +		bbWriteBits(c); +		c->bb_p = c->bb_op; +		c->bb_op += c->bb_c_s8; +		c->bb_b = bit; +		c->bb_k = 1; +	} +} + + +static void bbPutByte(struct ucl_compress *c, unsigned b) +{ +	/**printf("putbyte %p %p %x  (%d)\n", op, bb_p, x, bb_k);*/ +	assert(c->bb_p == NULL || c->bb_p + c->bb_c_s8 <= c->bb_op); +	*c->bb_op++ = (uint8_t)(b); +} + +static void bbFlushBits(struct ucl_compress *c, unsigned filler_bit) +{ +	if (c->bb_k > 0) +	{ +		assert(c->bb_k <= c->bb_c_s); +		while (c->bb_k != c->bb_c_s) +			bbPutBit(c, filler_bit); +		bbWriteBits(c); +		c->bb_k = 0; +	} +	c->bb_p = NULL; +} + + + +/*********************************************************************** +// +************************************************************************/ + + +static void code_prefix_ss11(struct ucl_compress *c, uint32_t i) +{ +	if (i >= 2) +	{ +		uint32_t t = 4; +		i += 2; +		do { +			t <<= 1; +		} while (i >= t); +		t >>= 1; +		do { +			t >>= 1; +			bbPutBit(c, (i & t) ? 1 : 0); +			bbPutBit(c, 0); +		} while (t > 2); +	} +	bbPutBit(c, (unsigned)i & 1); +	bbPutBit(c, 1); +} + +static void +code_match(struct ucl_compress *c, unsigned int m_len, const unsigned int m_off) + +{ +	while (m_len > c->conf.max_match) +	{ +		code_match(c, c->conf.max_match - 3, m_off); +		m_len -= c->conf.max_match - 3; +	} +	 +	c->match_bytes += m_len; +	if (m_len > c->result[3]) +		c->result[3] = m_len; +	if (m_off > c->result[1]) +		c->result[1] = m_off; + +	bbPutBit(c, 0); + +	if (m_off == c->last_m_off) +	{ +		bbPutBit(c, 0); +		bbPutBit(c, 1); +	} +	else +	{ +		code_prefix_ss11(c, 1 + ((m_off - 1) >> 8)); +		bbPutByte(c, (unsigned)m_off - 1); +	} +	m_len = m_len - 1 - (m_off > M2_MAX_OFFSET); +	if (m_len >= 4) +	{ +		bbPutBit(c,0); +		bbPutBit(c,0); +		code_prefix_ss11(c, m_len - 4); +	} +	else +	{ +		bbPutBit(c, m_len > 1); +		bbPutBit(c, (unsigned)m_len & 1); +	} + +	c->last_m_off = m_off; +} + +static void +code_run(struct ucl_compress *c, const uint8_t *ii, unsigned int lit) +{ +	if (lit == 0) +		return; +	c->lit_bytes += lit; +	if (lit > c->result[5]) +		c->result[5] = lit; +	do { +		bbPutBit(c, 1); +		bbPutByte(c, *ii++); +	} while (--lit > 0); +} + +/*********************************************************************** +// +************************************************************************/ + +static int +len_of_coded_match(struct ucl_compress *c, unsigned int m_len, unsigned int +	m_off) + +{ +	int b; +	if (m_len < 2 || (m_len == 2 && (m_off > M2_MAX_OFFSET)) +		|| m_off > c->conf.max_offset) +		return -1; +	assert(m_off > 0); +	 +	m_len = m_len - 2 - (m_off > M2_MAX_OFFSET); +	 +	if (m_off == c->last_m_off) +		b = 1 + 2; +	else +	{ +		b = 1 + 10; +		m_off = (m_off - 1) >> 8; +		while (m_off > 0) +		{ +			b += 2; +			m_off >>= 1; +		} +	} + +	b += 2; +	if (m_len < 3) +		return b; +	m_len -= 3; + +	do { +		b += 2; +		m_len >>= 1; +	} while (m_len > 0); + +	return b; +} + +int ucl_nrv2b_99_compress( +	const uint8_t *in, unsigned long in_len, +	uint8_t *out, unsigned long *out_len, +	unsigned int *result) +{ +	const uint8_t *ii; +	unsigned int lit; +	unsigned int m_len, m_off; +	struct ucl_compress c_buffer; +	struct ucl_compress * const c = &c_buffer; +	struct ucl_swd *swd; +	unsigned int result_buffer[16]; +	int r; + +/* max compression */ +#define SC_TRY_LAZY    2 +#define SC_GOOD_LENGTH F +#define SC_MAX_LAZY    F +#define SC_NICE_LENGTH F +#define SC_MAX_CHAIN   4096 +#define SC_FLAGS       1 +#define SC_MAX_OFFSET  N +	 +	memset(c, 0, sizeof(*c)); +	c->ip = c->in = in; +	c->in_end = in + in_len; +	c->out = out; +	c->result = result ? result : result_buffer; +	memset(c->result, 0, 16*sizeof(*c->result)); +	c->result[0] = c->result[2] = c->result[4] = UINT_MAX; +	result = NULL; +	memset(&c->conf, 0xff, sizeof(c->conf)); +	r = bbConfig(c, ENDIAN, BITSIZE); +	if (r == 0) +		r = bbConfig(c, c->conf.bb_endian, c->conf.bb_size); +	if (r != 0) +		return UCL_E_INVALID_ARGUMENT; +	c->bb_op = out; +	 +	ii = c->ip;             /* point to start of literal run */ +	lit = 0; +	 + +	swd = (struct ucl_swd *) malloc(sizeof(*swd)); +	if (!swd) +		return UCL_E_OUT_OF_MEMORY; + +	swd->f = F; +	swd->n = N; +	if (in_len >= 256 && in_len < swd->n) +		swd->n = in_len; +	if (swd->f < 8 || swd->n < 256) +		return UCL_E_INVALID_ARGUMENT; + +	r = init_match(c,swd,NULL,0, SC_FLAGS); +	if (r != UCL_E_OK) +	{ +		free(swd); +		return r; +	} +	if (SC_MAX_CHAIN > 0) +		swd->max_chain = SC_MAX_CHAIN; +	if (SC_NICE_LENGTH > 0) +		swd->nice_length = SC_NICE_LENGTH; +	if (c->conf.max_match < swd->nice_length) +		swd->nice_length = c->conf.max_match; +	 +	c->last_m_off = 1; +	r = find_match(c,swd,0,0); +	if (r != UCL_E_OK) +		return r; +	while (c->look > 0) +	{ +		unsigned int ahead; +		unsigned int max_ahead; +		int l1, l2; +		 +		c->codesize = c->bb_op - out; +		 +		m_len = c->m_len; +		m_off = c->m_off; +		 +		assert(c->bp == c->ip - c->look); +		assert(c->bp >= in); +		if (lit == 0) +			ii = c->bp; +		assert(ii + lit == c->bp); +		assert(swd->b_char == *(c->bp)); +		 +		if (m_len < 2 || (m_len == 2 && (m_off > M2_MAX_OFFSET)) +			|| m_off > c->conf.max_offset) +		{ +			/* a literal */ +			lit++; +			swd->max_chain = SC_MAX_CHAIN; +			r = find_match(c,swd,1,0); +			assert(r == 0); +			continue; +		} +		 +		/* a match */ +		assert_match(swd,m_len,m_off); +		 +		/* shall we try a lazy match ? */ +		ahead = 0; +		if (SC_TRY_LAZY <= 0 || m_len >= SC_MAX_LAZY || m_off == +			c->last_m_off) + +		{ +			/* no */ +			l1 = 0; +			max_ahead = 0; +		} +		else +		{ +			/* yes, try a lazy match */ +			l1 = len_of_coded_match(c,m_len,m_off); +			assert(l1 > 0); +			max_ahead = SC_TRY_LAZY; +			if ((m_len - 1) < max_ahead) { +				max_ahead = m_len -1; +			} +		} +		 +		while (ahead < max_ahead && c->look > m_len) +		{ +			if (m_len >= SC_GOOD_LENGTH) +				swd->max_chain = SC_MAX_CHAIN >> 2; +			else +				swd->max_chain = SC_MAX_CHAIN; +			r = find_match(c,swd,1,0); +			ahead++; +			 +			assert(r == 0); +			assert(c->look > 0); +			assert(ii + lit + ahead == c->bp); +			 +			if (c->m_len < 2) +				continue; +			l2 = len_of_coded_match(c,c->m_len,c->m_off); +			if (l2 < 0) +				continue; +			if (l1 + (int)(ahead + c->m_len - m_len) * 5 > l2 + +				(int)(ahead) * 9) +			{ +				c->lazy++; +				assert_match(swd,c->m_len,c->m_off); +				lit += ahead; +				assert(ii + lit == c->bp); +				goto lazy_match_done; +			} +		} +		 +		assert(ii + lit + ahead == c->bp); +		 +		/* 1 - code run */ +		code_run(c,ii,lit); +		lit = 0; +		 +		/* 2 - code match */ +		code_match(c,m_len,m_off); +		swd->max_chain = SC_MAX_CHAIN; +		r = find_match(c,swd,m_len,1+ahead); +		assert(r == 0); +		 +	lazy_match_done: ; +	} +	 +	/* store final run */ +	code_run(c,ii,lit); +	 +	/* EOF */ +	bbPutBit(c, 0); +	code_prefix_ss11(c, 0x1000000U); +	bbPutByte(c, 0xff); + +	bbFlushBits(c, 0); +	 +	assert(c->textsize == in_len); +	c->codesize = c->bb_op - out; +	*out_len = c->bb_op - out; +	 +#if 0 +	printf("%7ld %7ld -> %7ld   %7ld %7ld   %ld  (max: %d %d %d)\n", +		(long) c->textsize, (long) in_len, (long) c->codesize, +		c->match_bytes, c->lit_bytes,  c->lazy, +		c->result[1], c->result[3], c->result[5]); +#endif +	assert(c->lit_bytes + c->match_bytes == in_len); +	 +	swd_exit(swd); +	free(swd); + +	return UCL_E_OK; +} + + +void Encode(void)  /* compression */ +{ +	uint8_t *in, *out; +	unsigned long in_len, out_len; +	uint32_t tw; +	int r; +	fseek(infile, 0, SEEK_END); +	in_len = ftell(infile); +#ifdef VERBOSE +	if ((signed long)in_len < 0) +		Fprintf((stderr, "Errno: %d", errno)); +#endif +#if UCLPACK_COMPAT +	{ +		uint8_t byte; +		if (fwrite(magic, sizeof(magic), 1, outfile) != 1) +			Error("Can't write."); +		tw = htonl(0); /* flags */ +		if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +			Error("Can't write."); +		byte = 0x2b;		/* method */ +		if (fwrite(&byte, sizeof(byte), 1, outfile) != 1) +			Error("Can't write."); +		byte = 10;		/* level */ +		if (fwrite(&byte, sizeof(byte), 1, outfile) != 1) +			Error("Can't write."); +		tw = htonl(256*1024);		/* block_size */ +		if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +			Error("Can't write."); +		tw = htonl(in_len); +		if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +			Error("Can't write.");	/* output size of text */ +	} +#else +	tw = host_to_i86ul(in_len); +	if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +		Error("Can't write.");	/* output size of text */ +#endif	 +	if (in_len == 0) +		return; +	rewind(infile); + +	in = malloc(in_len); +	out_len = in_len + (in_len/8) + 256; +	out = malloc(out_len); +	if (!in || !out) { +		Error("Can't malloc"); +	} +	if (fread(in, in_len, 1, infile) != 1) { +		Error("Can't read"); +	} +	r = ucl_nrv2b_99_compress(in, in_len, out, &out_len, 0 ); +	if (r != UCL_E_OK) +		Error("Compression failure\n"); +#if UCLPACK_COMPAT +	tw = htonl(out_len); +	if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +		Error("Can't write.");	/* file size of text */ + +#endif +	if (fwrite(out, out_len, 1, outfile) != 1) { +		Error("Write error\n"); +	} +#if UCLPACK_COMPAT +	tw = htonl(0); /* EOF marker */ +	if (fwrite(&tw, sizeof(tw), 1, outfile) != 1) +		Error("Can't write."); + +#endif + +#ifdef	LONG_REPORT +	Fprintf((stdout, "input size    %ld bytes\n", in_len)); +	Fprintf((stdout, "output size   %ld bytes\n", out_len)); +	Fprintf((stdout, "input/output  %.3f\n", (double)in_len / out_len)); +#else +	Fprintf((stdout, "input/output = %ld/%ld = %.3f\n", in_len, out_len, +		(double)in_len / out_len)); +#endif +	 +} + +#endif + +#ifdef DECODE + +#define GETBIT_8(bb, src, ilen) \ +    (((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1) + +#define GETBIT_LE16(bb, src, ilen) \ +    (bb*=2,bb&0xffff ? (bb>>16)&1 : (ilen+=2,((bb=(src[ilen-2]+src[ilen-1]*256u)*2+1)>>16)&1)) + +#define GETBIT_LE32(bb, src, ilen) \ +    (bc > 0 ? ((bb>>--bc)&1) : (bc=31,\ +    bb=*(const uint32_t *)((src)+ilen),ilen+=4,(bb>>31)&1)) + +#define GETBIT_LE64(bb, src, ilen) \ +    (bc > 0 ? ((bb>>--bc)&1) : (bc=63, \ +    bb=*(const uint64_t *)((src)+ilen),ilen+=8,(bb>>63)&1)) + +#if ENDIAN == 0 && BITSIZE == 8 +#define GETBIT(bb, src, ilen) GETBIT_8(bb, src, ilen) +#endif +#if ENDIAN == 0 && BITSIZE == 16 +#define GETBIT(bb, src, ilen) GETBIT_LE16(bb, src, ilen) +#endif +#if ENDIAN == 0 && BITSIZE == 32 +#define GETBIT(bb, src, ilen) GETBIT_LE32(bb, src, ilen) +#endif +#if ENDIAN == 0 && BITSIZE == 64 +#define GETBIT(bb, src, ilen) GETBIT_LE64(bb, src, ilen) +#endif +#ifndef GETBIT +#error "Bad Combination of ENDIAN and BITSIZE values specified" +#endif + +#undef SAFE + +#ifdef SAFE +#define FAIL(x,r)   if (x) { Error(r); } +#else +#define FAIL(x,r) +#endif + +void Decode(void)  /* recover */ +{ +	uint32_t tw; +	uint8_t *src, *dst; +	unsigned long max_src_len, src_len, dst_len; +	unsigned long ilen = 0, olen = 0, last_m_off =  1; +#if BITSIZE <= 32 +	uint32_t bb = 0; +#elif BITSIZE == 64 +	uint64_t bb = 0; +#endif +	unsigned bc = 0; +#if UCLPACK_COMPAT +	if (fseek(infile, sizeof(magic) + sizeof(tw) + 1 + 1 + sizeof(tw), +		SEEK_SET) != 0) + +		Error("Seek Error"); +	if (fread(&tw, sizeof(tw), 1, infile) < 1) +		Error("Can't read"); /* read size of text */ +	dst_len = ntohl(tw); +	if (fread(&tw, sizeof(tw), 1, infile) < 1) +		Error("Can't read"); /* read size of file */ +	max_src_len = ntohl(tw); +#else +	if (fread(&tw, sizeof(tw), 1, infile) < 1) +		Error("Can't read"); /* read size of text */ +	dst_len = i86ul_to_host(tw); +	max_src_len = dst_len + (dst_len/8) + 256; +#endif +	if (dst_len == 0) +		return; +	dst = malloc(dst_len); +	if (!dst) +		Error("Can't malloc"); +	src = malloc(max_src_len); +	if (!src) +		Error("Can't malloc"); +	src_len = fread(src, 1, max_src_len, infile); +	if (src_len <= 0)  +		Error("Can't read"); + +	for(;;) { +		unsigned int m_off, m_len; +		while(GETBIT(bb, src, ilen)) { +			FAIL(ilen >= src_len, "input overrun"); +			FAIL(olen >= dst_len, "output  overrun"); +			dst[olen++] = src[ilen++]; +		} +		m_off = 1; +		do { +			m_off = m_off*2 + GETBIT(bb, src, ilen); +			FAIL(ilen >= src_len, "input overrun"); +			FAIL(m_off > 0xffffffU +3, "lookbehind overrun"); +		} while (!GETBIT(bb, src, ilen)); +		if (m_off == 2) +		{ +			m_off = last_m_off; +		} +		else +		{ +			FAIL(ilen >= src_len, "input overrun"); +			m_off = (m_off - 3)*256 + src[ilen++]; +			if (m_off == 0xffffffffU) +				break; +			last_m_off = ++m_off; +		} +		m_len = GETBIT(bb, src, ilen); +		m_len = m_len*2 + GETBIT(bb, src, ilen); +		if (m_len == 0)  +		{ +			m_len++; +			do { +				m_len = m_len*2 + GETBIT(bb, src, ilen); +				FAIL(ilen >= src_len, "input overrun"); +				FAIL(m_len >= dst_len, "output overrun"); +			} while(!GETBIT(bb, src, ilen)); +			m_len += 2; +		} +		m_len += (m_off > 0xd00); +		FAIL(olen + m_len > dst_len, "output overrun"); +		FAIL(m_off > olen, "lookbeind overrun"); +		{ +			const uint8_t *m_pos; +			m_pos = dst + olen - m_off; +			dst[olen++] = *m_pos++; +			do { +				dst[olen++] = *m_pos++; +			} while(--m_len > 0); +		} +	} +	FAIL(ilen < src_len, "input not consumed"); +	FAIL(ilen > src_len, "input overrun"); +	assert(ilen == src_len); +	Fprintf((stderr, "%12ld\n", olen)); +	if (dst_len != olen) { +		fprintf(stderr, "length != expected length\n"); +	} +	if (fwrite(dst, olen, 1, outfile) != 1) +		Error("Write error\n"); +	free(src); +	free(dst); +} +#endif + +#ifdef MAIN +int main(int argc, char *argv[]) +{ +	char  *s; +	FILE  *f; +	int    c; +	 +	if (argc == 2) { +		outfile = stdout; +		if ((f = tmpfile()) == NULL) { +			perror("tmpfile"); +			return EXIT_FAILURE; +		} +		while ((c = getchar()) != EOF) +			fputc(c, f); +		rewind(infile = f); +	} +	else if (argc != 4) { +		Fprintf((stderr, "'nrv2b e file1 file2' encodes file1 into file2.\n" +			"'nrv2b d file2 file1' decodes file2 into file1.\n")); +		return EXIT_FAILURE; +	} +	if (argc == 4) { +		if ((s = argv[1], s[1] || strpbrk(s, "DEde") == NULL) +			|| (s = argv[2], (infile  = fopen(s, "rb")) == NULL) +			|| (s = argv[3], (outfile = fopen(s, "wb")) == NULL)) { +			Fprintf((stderr, "??? %s\n", s)); +			return EXIT_FAILURE; +		} +	} +	if (toupper(*argv[1]) == 'E') +		Encode(); +	else +		Decode(); +	fclose(infile); +	fclose(outfile); +	return EXIT_SUCCESS; +} +#endif diff --git a/roms/ipxe/src/util/padimg.pl b/roms/ipxe/src/util/padimg.pl new file mode 100755 index 00000000..4421aaf4 --- /dev/null +++ b/roms/ipxe/src/util/padimg.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use Getopt::Long; +use Fcntl; + +my $verbosity = 0; +my $blksize = 512; +my $byte = 0; + +my %opts = ( +  'verbose|v+' => sub { $verbosity++; }, +  'quiet|q+' => sub { $verbosity--; }, +  'blksize|s=o' => sub { $blksize = $_[1]; }, +  'byte|b=o' => sub { $byte = $_[1]; }, +); + +Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); +GetOptions ( %opts ) or die "Could not parse command-line options\n"; + +while ( my $filename = shift ) { +  die "$filename is not a file\n" unless -f $filename; +  my $oldsize = -s $filename; +  my $padsize = ( ( -$oldsize ) % $blksize ); +  my $newsize = ( $oldsize + $padsize ); +  next unless $padsize; +  if ( $verbosity >= 1 ) { +      printf "Padding %s from %d to %d bytes with %d x 0x%02x\n", +	     $filename, $oldsize, $newsize, $padsize, $byte; +  } +  if ( $byte ) { +    sysopen ( my $fh, $filename, ( O_WRONLY | O_APPEND ) ) +	or die "Could not open $filename for appending: $!\n"; +    syswrite $fh, ( chr ( $byte ) x $padsize ) +	or die "Could not append to $filename: $!\n"; +    close ( $fh ); +  } else { +    truncate $filename, $newsize +	or die "Could not resize $filename: $!\n"; +  } +  die "Failed to pad $filename\n" +      unless ( ( ( -s $filename ) % $blksize ) == 0 ); +} diff --git a/roms/ipxe/src/util/parserom.pl b/roms/ipxe/src/util/parserom.pl new file mode 100755 index 00000000..e278e633 --- /dev/null +++ b/roms/ipxe/src/util/parserom.pl @@ -0,0 +1,66 @@ +#!/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 +# +# Based upon portions of Ken Yap's genrules.pl + +use strict; +use warnings; + +die "Syntax: $0 driver_source.c" unless @ARGV == 1; +my $source = shift; +open DRV, "<$source" or die "Could not open $source: $!\n"; + +( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ ) +    or die "Could not parse source file name \"$source\"\n"; + +my $printed_family; + +sub rom { +  ( my $type, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; +  my $ids = $vendor ? "$vendor,$device" : "-"; +  unless ( $printed_family ) { +    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; +} + +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"; +  } +} + +close DRV; diff --git a/roms/ipxe/src/util/romcheck.pl b/roms/ipxe/src/util/romcheck.pl new file mode 100755 index 00000000..f47bb07e --- /dev/null +++ b/roms/ipxe/src/util/romcheck.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use constant DEVICES => "/proc/bus/pci/devices"; + +open my $fh, DEVICES +    or die "Could not open ".DEVICES.": $!"; + +while ( ( my $line = <$fh> ) ) { + +  # Parse line from /proc/bus/pci/devices +  chomp $line; +  ( my $bus, my $devfn, my $vendor, my $device, my $irq, my $bars, my $lengths, +    my $driver ) +      = ( $line =~ /^ ([0-9a-f]{2}) ([0-9a-f]{2}) \s+ +		      ([0-9a-f]{4}) ([0-9a-f]{4}) \s+ ([0-9a-f]+) \s+ +		      ((?:[0-9a-f]+\s+){7}) ((?:[0-9a-f]+\s+){7}) +		      (.+)?$/x ) +      or die "Invalid line \"".$line."\"\n"; +  ( $bus, $devfn, $vendor, $device, $irq ) = +      map { hex ( $_ ) } ( $bus, $devfn, $vendor, $device, $irq ); +  my $dev = ( $devfn >> 3 ); +  my $fn = ( $devfn & 0x7 ); +  $bars = [ map { hex ( $_ ) } split ( /\s+/, $bars ) ]; +  $lengths = [ map { hex ( $_ ) } split ( /\s+/, $lengths ) ]; + +  # Calculate expansion ROM BAR presence and length +  my $rom_length = $lengths->[6]; + +  # Look for a BAR that could support a .mrom +  my $mrom_ok; +  if ( $rom_length ) { +    for ( my $bar = 0 ; $bar < 7 ; $bar++ ) { +      # Skip I/O BARs +      next if $bars->[$bar] & 0x01; +      # Skip low half of 64-bit BARs +      $bar++ if $bars->[$bar] & 0x04; +      # Skip 64-bit BARs with high dword set +      next if $bars->[$bar] >> 32; +      # Skip BARs smaller than the expansion ROM BAR +      next if $lengths->[$bar] < $rom_length; +      # This BAR is usable! +      $mrom_ok = 1; +      last; +    } +  } + +  printf "%02x:%02x.%x (%04x:%04x)", $bus, $dev, $fn, $vendor, $device; +  printf " supports a %dkB .rom", ( $rom_length / 1024 ) if $rom_length; +  printf " or .mrom" if $mrom_ok; +  printf "\n"; +} diff --git a/roms/ipxe/src/util/sortobjdump.pl b/roms/ipxe/src/util/sortobjdump.pl new file mode 100755 index 00000000..1373a7ff --- /dev/null +++ b/roms/ipxe/src/util/sortobjdump.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +# Sort the symbol table portion of the output of objdump -ht by +# section, then by symbol value, then by size.  Used to enhance the +# linker maps produced by "make bin/%.map" by also showing the values +# of all non-global symbols. + +my %section_idx = ( "*ABS*" => ".", "*UND*" => "_" ); +my %lines; +while ( <> ) { +  if ( /^\s+(\d+)\s+([\.\*]\S+)\s+[0-9a-fA-F]+\s+[0-9a-fA-F]/ ) { +    # It's a header line containing a section definition; extract the +    # section index and store it.  Also print the header line. +    print; +    ( my $index, my $section ) = ( $1, $2 ); +    $section_idx{$section} = sprintf ( "%02d", $index ); +  } elsif ( /^([0-9a-fA-F]+)\s.*?\s([\.\*]\S+)\s+([0-9a-fA-F]+)\s+(\S+)/ ) { +    # It's a symbol line - store it in the hash, indexed by +    # "<section_index>:<value>:<size>:<end_tag>".  <end_tag> is "0" if +    # the symbol name is of the form xxx_end, "1" otherwise; this is +    # done so that table end markers show up before any other symbols +    # with the same value. +    ( my $value, my $section, my $size, my $name ) = ( $1, $2, $3, $4 ); +    die "Unrecognised section \"$section\"\n" +	unless exists $section_idx{$section}; +    my $section_idx = $section_idx{$section}; +    my $end = ( $name =~ /_end$/ ) ? "0" : "1"; +    my $key = $section_idx.":".$value.":".$size.":".$end; +    $lines{$key} ||= ''; +    $lines{$key} .= $_; +  } else { +    # It's a generic header line: just print it. +    print; +  } +} + +print $lines{$_} foreach sort keys %lines; diff --git a/roms/ipxe/src/util/swapdevids.pl b/roms/ipxe/src/util/swapdevids.pl new file mode 100755 index 00000000..c6255ae7 --- /dev/null +++ b/roms/ipxe/src/util/swapdevids.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl -w +# +#	Program to reverse the device identifier IDs in the PCIR and PnP +#	structures in a ROM for old non-compliant BIOSes +# +#	GPL, Ken Yap 2001 +# + +use bytes; + +use IO::Seekable; + +sub swaplocs ($$$) +{ +	my ($dataref, $loc1, $loc2) = @_; +	my ($t); + +	$t = substr($$dataref, $loc1, 1); +	substr($$dataref, $loc1, 1) = substr($$dataref, $loc2, 1); +	substr($$dataref, $loc2, 1) = $t; +} + +sub printdevids ($$) +{ +	my ($dataref, $loc) = @_; + +	return (sprintf "%02x %02x %02x", unpack('C3', substr($$dataref, $loc, 3))); +} + +$#ARGV >= 0 or die "Usage: $0 romimage\n"; +$file = $ARGV[0]; +open(F, "+<$file") or die "$file: $!\n"; +binmode(F); +# Handle up to 64kB ROM images +$len = read(F, $data, 64*1024); +defined($len) or die "$file: $!\n"; +substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n"; +($pci, $pnp) = unpack('v2', substr($data, 0x18, 4)); +($pci < $len and $pnp < $len) or die "$file: Not a PCI PnP ROM image\n"; +(substr($data, $pci, 4) eq 'PCIR' and substr($data, $pnp, 4) eq '$PnP') +	or die "$file: No PCI and PNP structures, not a PCI PNP ROM image\n"; +&swaplocs(\$data, $pci+13, $pci+15); +&swaplocs(\$data, $pnp+18, $pnp+20); +seek(F, 0, SEEK_SET) or die "$file: Cannot seek to beginning\n"; +print F $data; +close(F); +print "PCI devids now: ", &printdevids(\$data, $pci+13), "\n"; +print "PnP devids now: ", &printdevids(\$data, $pnp+18), "\n"; +exit(0); diff --git a/roms/ipxe/src/util/symcheck.pl b/roms/ipxe/src/util/symcheck.pl new file mode 100755 index 00000000..8925ca62 --- /dev/null +++ b/roms/ipxe/src/util/symcheck.pl @@ -0,0 +1,191 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use constant WARNING_SIZE => 512; + +my $symtab = {}; + +# Scan output of "objdump -w -t bin/blib.a" and build up symbol table +# +my $object; +while ( <> ) { +  chomp; +  if ( /^In archive/ ) { +    # Do nothing +  } elsif ( /^$/ ) { +    # Do nothing +  } elsif ( /^(\S+\.o):\s+file format/ ) { +    $object = $1; +  } elsif ( /^SYMBOL TABLE:/ ) { +    # Do nothing +  } elsif ( /^([0-9a-fA-F]+)\s(l|g|\s)......\s(\S+)\s+([0-9a-fA-F]+)\s+(\S+)$/ ) { +    my $value = $1; +    my $scope = $2; +    my $section = $3; +    my $size = $4; +    my $symbol = $5; +    $symtab->{$object}->{$symbol} = { +      global	=> ( $scope ne "l" ), +      section	=> ( $section eq "*UND*" ? undef : $section ), +      value	=> ( $value ? hex ( $value ) : 0 ), +      size	=> ( $size ? hex ( $size ) : 0 ), +    }; +  } else { +    die "Unrecognized line \"$_\""; +  } +} + +# Add symbols that we know will be generated or required by the linker +# +foreach my $object ( keys %$symtab ) { +  my $obj_symbol = "obj_$object"; +  $obj_symbol =~ s/\.o$//; +  $obj_symbol =~ s/\W/_/g; +  $symtab->{LINKER}->{$obj_symbol} = { +    global	=> 1, +    section	=> undef, +    value	=> 0, +    size	=> 0, +  }; +} +foreach my $link_sym qw ( __prefix _prefix _prefix_load_offset +			  _prefix_size _prefix_progbits_size _prefix_size_pgh +			  __text16 _text16 _text16_load_offset +			  _text16_size _text16_progbits_size _text16_size_pgh +			  __data16 _data16 _data16_load_offset +			  _data16_size _data16_progbits_size _data16_size_pgh +			  __text _text __data _data _textdata_load_offset +			  _textdata_size _textdata_progbits_size +			  __rodata __bss _end +			  _payload_offset _max_align +			  _load_size _load_size_pgh _load_size_sect +			  pci_vendor_id pci_device_id ) { +  $symtab->{LINKER}->{$link_sym} = { +    global	=> 1, +    section	=> '*ABS*', +    value	=> 0, +    size	=> 0, +  }; +} + +# Add symbols that we know will be used by the debug system +# +foreach my $debug_sym qw ( dbg_autocolourise dbg_decolourise +			   dbg_hex_dump_da ) { +  $symtab->{DEBUG}->{$debug_sym} = { +    global	=> 1, +    section	=> undef, +    value	=> 0, +    size	=> 0, +  }; +} + +# Build up requires, provides and shares symbol tables for global +# symbols +# +my $globals = {}; +while ( ( my $object, my $symbols ) = each %$symtab ) { +  while ( ( my $symbol, my $info ) = each %$symbols ) { +    if ( $info->{global} ) { +      my $category; +      if ( ! defined $info->{section} ) { +	$category = "requires"; +      } elsif ( $info->{section} eq "*COM*" ) { +	$category = "shares"; +      } else { +	$category = "provides"; +      } +      $globals->{$symbol}->{$category}->{$object} = 1; +    } +  } +} + +# Check for multiply defined, never-defined and unused global symbols +# +my $problems = {}; +while ( ( my $symbol, my $info ) = each %$globals ) { +  my @provides = keys %{$info->{provides}}; +  my @requires = keys %{$info->{requires}}; +  my @shares = keys %{$info->{shares}}; + +  if ( ( @provides == 0 ) && ( @shares == 1 ) ) { +    # A symbol "shared" by just a single file is actually being +    # provided by that file; it just doesn't have an initialiser. +    @provides = @shares; +    @shares = (); +  } + +  if ( ( @requires > 0 ) && ( @provides == 0 ) && ( @shares == 0 ) ) { +    # No object provides this symbol, but some objects require it. +    $problems->{$_}->{nonexistent}->{$symbol} = 1 foreach @requires; +  } + +  if ( ( @requires == 0 ) && ( @provides > 0 ) ) { +    # No object requires this symbol, but some objects provide it. +    foreach my $provide ( @provides ) { +      if ( $provide eq "LINKER" ) { +	# Linker-provided symbols are exempt from this check. +      } elsif ( $symtab->{$provide}->{$symbol}->{section} =~ /^\.tbl\./ ) { +	# Linker tables are exempt from this check. +      } else { +	$problems->{$provide}->{unused}->{$symbol} = 1; +      } +    } +  } + +  if ( ( @shares > 0 ) && ( @provides > 0 ) ) { +    # A shared symbol is being initialised by an object +    $problems->{$_}->{shared}->{$symbol} = 1 foreach @provides; +  } + +  if ( @provides > 1 ) { +    # A non-shared symbol is defined in multiple objects +    $problems->{$_}->{multiples}->{$symbol} = 1 foreach @provides; +  } +} + +# Check for excessively large local symbols.  Text and rodata symbols +# are exempt from this check +# +while ( ( my $object, my $symbols ) = each %$symtab ) { +  while ( ( my $symbol, my $info ) = each %$symbols ) { +    if ( ( ! $info->{global} ) && +	 ( ( defined $info->{section} ) && +	   ! ( $info->{section} =~ /^(\.text|\.rodata)/ ) ) && +	 ( $info->{size} >= WARNING_SIZE ) ) { +      $problems->{$object}->{large}->{$symbol} = 1; +    } +  } +} + +# Print out error messages +# +my $errors = 0; +my $warnings = 0; +foreach my $object ( sort keys %$problems ) { +  my @nonexistent = sort keys %{$problems->{$object}->{nonexistent}}; +  my @multiples = sort keys %{$problems->{$object}->{multiples}}; +  my @unused = sort keys %{$problems->{$object}->{unused}}; +  my @shared = sort keys %{$problems->{$object}->{shared}}; +  my @large = sort keys %{$problems->{$object}->{large}}; + +  print "WARN $object provides unused symbol $_\n" foreach @unused; +  $warnings += @unused; +  print "WARN $object has large static symbol $_\n" foreach @large; +  $warnings += @large; +  print "ERR  $object requires non-existent symbol $_\n" foreach @nonexistent; +  $errors += @nonexistent; +  foreach my $symbol ( @multiples ) { +    my @other_objects = sort grep { $_ ne $object } +		        keys %{$globals->{$symbol}->{provides}}; +    print "ERR  $object provides symbol $symbol" +	." (also provided by @other_objects)\n"; +  } +  $errors += @multiples; +  print "ERR  $object misuses shared symbol $_\n" foreach @shared; +} + +print "$errors error(s), $warnings warning(s)\n"; +exit ( $errors ? 1 : 0 ); diff --git a/roms/ipxe/src/util/zbin.c b/roms/ipxe/src/util/zbin.c new file mode 100644 index 00000000..3b7cf95b --- /dev/null +++ b/roms/ipxe/src/util/zbin.c @@ -0,0 +1,491 @@ +#include <stdio.h> +#include <sys/stat.h> + +#define ENCODE +#define VERBOSE +#include "nrv2b.c" +FILE *infile, *outfile; + +#define DEBUG 0 + +struct input_file { +	void *buf; +	size_t len; +}; + +struct output_file { +	void *buf; +	size_t len; +	size_t hdr_len; +	size_t max_len; +}; + +struct zinfo_common { +	char type[4]; +	char pad[12]; +}; + +struct zinfo_copy { +	char type[4]; +	uint32_t offset; +	uint32_t len; +	uint32_t align; +}; + +struct zinfo_pack { +	char type[4]; +	uint32_t offset; +	uint32_t len; +	uint32_t align; +}; + +struct zinfo_payload { +	char type[4]; +	uint32_t pad1; +	uint32_t pad2; +	uint32_t align; +}; + +struct zinfo_add { +	char type[4]; +	uint32_t offset; +	uint32_t divisor; +	uint32_t pad; +}; + +union zinfo_record { +	struct zinfo_common common; +	struct zinfo_copy copy; +	struct zinfo_pack pack; +	struct zinfo_payload payload; +	struct zinfo_add add; +}; + +struct zinfo_file { +	union zinfo_record *zinfo; +	unsigned int num_entries; +}; + +static unsigned long align ( unsigned long value, unsigned long align ) { +	return ( ( value + align - 1 ) & ~( align - 1 ) ); +} + +static int read_file ( const char *filename, void **buf, size_t *len ) { +	FILE *file; +	struct stat stat; + +	file = fopen ( filename, "r" ); +	if ( ! file ) { +		fprintf ( stderr, "Could not open %s: %s\n", filename, +			  strerror ( errno ) ); +		goto err; +	} + +	if ( fstat ( fileno ( file ), &stat ) < 0 ) { +		fprintf ( stderr, "Could not stat %s: %s\n", filename, +			  strerror ( errno ) ); +		goto err; +	} + +	*len = stat.st_size; +	*buf = malloc ( *len ); +	if ( ! *buf ) { +		fprintf ( stderr, "Could not malloc() %zd bytes for %s: %s\n", +			  *len, filename, strerror ( errno ) ); +		goto err; +	} + +	if ( fread ( *buf, 1, *len, file ) != *len ) { +		fprintf ( stderr, "Could not read %zd bytes from %s: %s\n", +			  *len, filename, strerror ( errno ) ); +		goto err; +	} + +	fclose ( file ); +	return 0; + + err: +	if ( file ) +		fclose ( file ); +	return -1; +} + +static int read_input_file ( const char *filename, +			     struct input_file *input ) { +	return read_file ( filename, &input->buf, &input->len ); +} + +static int read_zinfo_file ( const char *filename, +			     struct zinfo_file *zinfo ) { +	void *buf; +	size_t len; + +	if ( read_file ( filename, &buf, &len ) < 0 ) +		return -1; + +	if ( ( len % sizeof ( *(zinfo->zinfo) ) ) != 0 ) { +		fprintf ( stderr, ".zinfo file %s has invalid length %zd\n", +			  filename, len ); +		return -1; +	} + +	zinfo->zinfo = buf; +	zinfo->num_entries = ( len / sizeof ( *(zinfo->zinfo) ) ); +	return 0; +} + +static int alloc_output_file ( size_t max_len, struct output_file *output ) { +	output->len = 0; +	output->max_len = ( max_len ); +	output->buf = malloc ( max_len ); +	if ( ! output->buf ) { +		fprintf ( stderr, "Could not allocate %zd bytes for output\n", +			  max_len ); +		return -1; +	} +	memset ( output->buf, 0xff, max_len ); +	return 0; +} + +static int process_zinfo_copy ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	struct zinfo_copy *copy = &zinfo->copy; +	size_t offset = copy->offset; +	size_t len = copy->len; + +	if ( ( offset + len ) > input->len ) { +		fprintf ( stderr, "Input buffer overrun on copy\n" ); +		return -1; +	} + +	output->len = align ( output->len, copy->align ); +	if ( ( output->len + len ) > output->max_len ) { +		fprintf ( stderr, "Output buffer overrun on copy\n" ); +		return -1; +	} + +	if ( DEBUG ) { +		fprintf ( stderr, "COPY [%#zx,%#zx) to [%#zx,%#zx)\n", +			  offset, ( offset + len ), output->len, +			  ( output->len + len ) ); +	} + +	memcpy ( ( output->buf + output->len ), +		 ( input->buf + offset ), len ); +	output->len += len; +	return 0; +} + +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; + +	if ( ( offset + len ) > input->len ) { +		fprintf ( stderr, "Input buffer overrun on pack\n" ); +		return -1; +	} + +	output->len = align ( output->len, pack->align ); +	if ( output->len > output->max_len ) { +		fprintf ( stderr, "Output buffer overrun on pack\n" ); +		return -1; +	} + +	if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, +				     ( output->buf + output->len ), +				     &packed_len, 0 ) != UCL_E_OK ) { +		fprintf ( stderr, "Compression failure\n" ); +		return -1; +	} + +	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; +	if ( output->len > output->max_len ) { +		fprintf ( stderr, "Output buffer overrun on pack\n" ); +		return -1; +	} + +	return 0; +} + +static int process_zinfo_payl ( struct input_file *input +					__attribute__ (( unused )), +				struct output_file *output, +				union zinfo_record *zinfo ) { +	struct zinfo_payload *payload = &zinfo->payload; + +	output->len = align ( output->len, payload->align ); +	output->hdr_len = output->len; + +	if ( DEBUG ) { +		fprintf ( stderr, "PAYL at %#zx\n", output->hdr_len ); +	} +	return 0; +} + +static int process_zinfo_add ( struct input_file *input +					__attribute__ (( unused )), +			       struct output_file *output, +			       size_t len, +			       struct zinfo_add *add, size_t offset, +			       size_t datasize ) { +	void *target; +	signed long addend; +	unsigned long size; +	signed long val; +	unsigned long mask; + +	offset += add->offset; +	if ( ( offset + datasize ) > output->len ) { +		fprintf ( stderr, "Add at %#zx outside output buffer\n", +			  offset ); +		return -1; +	} + +	target = ( output->buf + offset ); +	size = ( align ( len, add->divisor ) / add->divisor ); + +	switch ( datasize ) { +	case 1: +		addend = *( ( int8_t * ) target ); +		break; +	case 2: +		addend = *( ( int16_t * ) target ); +		break; +	case 4: +		addend = *( ( int32_t * ) target ); +		break; +	default: +		fprintf ( stderr, "Unsupported add datasize %zd\n", +			  datasize ); +		return -1; +	} + +	val = size + addend; + +	/* The result of 1UL << ( 8 * sizeof(unsigned long) ) is undefined */ +	mask = ( ( datasize < sizeof ( mask ) ) ? +		 ( ( 1UL << ( 8 * datasize ) ) - 1 ) : ~0UL ); + +	if ( val < 0 ) { +		fprintf ( stderr, "Add %s%#x+%#lx at %#zx %sflows field\n", +			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ), size, +			  offset, ( ( addend < 0 ) ? "under" : "over" ) ); +		return -1; +	} + +	if ( val & ~mask ) { +		fprintf ( stderr, "Add %s%#x+%#lx at %#zx overflows %zd-byte " +			  "field (%d bytes too big)\n", +			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ), size, +			  offset, datasize, +			  ( int )( ( val - mask - 1 ) * add->divisor ) ); +		return -1; +	} + +	switch ( datasize ) { +	case 1: +		*( ( uint8_t * ) target ) = val; +		break; +	case 2: +		*( ( uint16_t * ) target ) = val; +		break; +	case 4: +		*( ( uint32_t * ) target ) = val; +		break; +	} + +	if ( DEBUG ) { +		fprintf ( stderr, "ADDx [%#zx,%#zx) (%s%#x+(%#zx/%#x)) = " +			  "%#lx\n", offset, ( offset + datasize ), +			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ), +			  len, add->divisor, val ); +	} + +	return 0; +} + +static int process_zinfo_addb ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->len, +				   &zinfo->add, 0, 1 ); +} + +static int process_zinfo_addw ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->len, +				   &zinfo->add, 0, 2 ); +} + +static int process_zinfo_addl ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->len, +				   &zinfo->add, 0, 4 ); +} + +static int process_zinfo_adhb ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->hdr_len, +				   &zinfo->add, 0, 1 ); +} + +static int process_zinfo_adhw ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->hdr_len, +				   &zinfo->add, 0, 2 ); +} + +static int process_zinfo_adhl ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, output->hdr_len, +				   &zinfo->add, 0, 4 ); +} + +static int process_zinfo_adpb ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, 0, 1 ); +} + +static int process_zinfo_adpw ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, 0, 2 ); +} + +static int process_zinfo_adpl ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, 0, 4 ); +} + +static int process_zinfo_appb ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, output->hdr_len, 1 ); +} + +static int process_zinfo_appw ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, output->hdr_len, 2 ); +} + +static int process_zinfo_appl ( struct input_file *input, +				struct output_file *output, +				union zinfo_record *zinfo ) { +	return process_zinfo_add ( input, output, +				   ( output->len - output->hdr_len ), +				   &zinfo->add, output->hdr_len, 4 ); +} + +struct zinfo_processor { +	char *type; +	int ( * process ) ( struct input_file *input, +			    struct output_file *output, +			    union zinfo_record *zinfo ); +}; + +static struct zinfo_processor zinfo_processors[] = { +	{ "COPY", process_zinfo_copy }, +	{ "PACK", process_zinfo_pack }, +	{ "PAYL", process_zinfo_payl }, +	{ "ADDB", process_zinfo_addb }, +	{ "ADDW", process_zinfo_addw }, +	{ "ADDL", process_zinfo_addl }, +	{ "ADHB", process_zinfo_adhb }, +	{ "ADHW", process_zinfo_adhw }, +	{ "ADHL", process_zinfo_adhl }, +	{ "ADPB", process_zinfo_adpb }, +	{ "ADPW", process_zinfo_adpw }, +	{ "ADPL", process_zinfo_adpl }, +	{ "APPB", process_zinfo_appb }, +	{ "APPW", process_zinfo_appw }, +	{ "APPL", process_zinfo_appl }, +}; + +static int process_zinfo ( struct input_file *input, +			   struct output_file *output, +			   union zinfo_record *zinfo ) { +	struct zinfo_common *common = &zinfo->common; +	struct zinfo_processor *processor; +	char type[ sizeof ( common->type ) + 1 ] = ""; +	unsigned int i; + +	strncat ( type, common->type, sizeof ( type ) - 1 ); +	for ( i = 0 ; i < ( sizeof ( zinfo_processors ) / +			    sizeof ( zinfo_processors[0] ) ) ; i++ ) { +		processor = &zinfo_processors[i]; +		if ( strcmp ( processor->type, type ) == 0 ) +			return processor->process ( input, output, zinfo ); +	} + +	fprintf ( stderr, "Unknown zinfo record type \"%s\"\n", &type[0] ); +	return -1; +} + +static int write_output_file ( struct output_file *output ) { +	if ( fwrite ( output->buf, 1, output->len, stdout ) != output->len ) { +		fprintf ( stderr, "Could not write %zd bytes of output: %s\n", +			  output->len, strerror ( errno ) ); +		return -1; +	} +	return 0; +} + +int main ( int argc, char **argv ) { +	struct input_file input; +	struct output_file output; +	struct zinfo_file zinfo; +	unsigned int i; + +	if ( argc != 3 ) { +		fprintf ( stderr, "Syntax: %s file.bin file.zinfo " +			  "> file.zbin\n", argv[0] ); +		exit ( 1 ); +	} + +	if ( read_input_file ( argv[1], &input ) < 0 ) +		exit ( 1 ); +	if ( read_zinfo_file ( argv[2], &zinfo ) < 0 ) +		exit ( 1 ); +	if ( alloc_output_file ( ( input.len * 4 ), &output ) < 0 ) +		exit ( 1 ); + +	for ( i = 0 ; i < zinfo.num_entries ; i++ ) { +		if ( process_zinfo ( &input, &output, +				     &zinfo.zinfo[i] ) < 0 ) +			exit ( 1 ); +	} + +	if ( write_output_file ( &output ) < 0 ) +		exit ( 1 ); + +	return 0; +} | 
