mirror of https://github.com/ipxe/ipxe.git
[util] Add Option::ROM library and rewrite disrom.pl to use it.
The Option::ROM module provides an easy way to read and edit fields within option ROM headers.pull/1/head
parent
e6cd16946a
commit
6f73bb5e00
|
@ -0,0 +1,459 @@
|
||||||
|
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., 675 Mass Ave, Cambridge, MA 02139, 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} );
|
||||||
|
return unpack ( $self->{fields}->{$key}->{pack}, $raw );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub STORE {
|
||||||
|
my $self = shift;
|
||||||
|
my $key = shift;
|
||||||
|
my $value = shift;
|
||||||
|
|
||||||
|
croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key );
|
||||||
|
my $raw = pack ( $self->{fields}->{$key}->{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 PNP_SIGNATURE => '$PnP';
|
||||||
|
|
||||||
|
our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PNP_SIGNATURE );
|
||||||
|
our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
|
||||||
|
|
||||||
|
=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" },
|
||||||
|
checksum => { offset => 0x06, length => 0x01, pack => "C" },
|
||||||
|
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<< 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, ( 128 * 1024 ); # 128kB is theoretical max size
|
||||||
|
$self->{data} = \$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: $!";
|
||||||
|
print $fh ${$self->{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<< checksum () >>
|
||||||
|
|
||||||
|
Calculate the byte checksum of the ROM.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub checksum {
|
||||||
|
my $hash = shift;
|
||||||
|
my $self = tied(%$hash);
|
||||||
|
|
||||||
|
return unpack ( "%8C*", ${$self->{data}} );
|
||||||
|
}
|
||||||
|
|
||||||
|
=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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -1,114 +1,75 @@
|
||||||
#!/usr/bin/perl -w
|
#!/usr/bin/perl -w
|
||||||
#
|
#
|
||||||
# Program to display key information about a boot ROM
|
# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
|
||||||
# including PCI and PnP structures
|
|
||||||
#
|
#
|
||||||
# GPL, Ken Yap 2001
|
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
use bytes;
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
sub getid ($)
|
use FindBin;
|
||||||
{
|
use lib "$FindBin::Bin";
|
||||||
my ($offset) = @_;
|
use Option::ROM qw ( :all );
|
||||||
|
|
||||||
return '' if ($offset == 0 or $offset > $len);
|
my $romfile = shift || "-";
|
||||||
my ($string) = unpack('Z32', substr($data, $offset, 32));
|
my $rom = new Option::ROM;
|
||||||
return ($string);
|
$rom->load ( $romfile );
|
||||||
|
|
||||||
|
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 " Length:\t0x%02x (%d)\n", $rom->{length}, ( $rom->{length} * 512 );
|
||||||
|
printf " Checksum:\t0x%02x (0x%02x)\n", $rom->{checksum}, $rom->checksum;
|
||||||
|
printf " UNDI header:\t0x%04x\n", $rom->{undi_header};
|
||||||
|
printf " PCI header:\t0x%04x\n", $rom->{pci_header};
|
||||||
|
printf " PnP header:\t0x%04x\n", $rom->{pnp_header};
|
||||||
|
printf "\n";
|
||||||
|
|
||||||
|
my $pci = $rom->pci_header();
|
||||||
|
if ( $pci ) {
|
||||||
|
printf "PCI header:\n\n";
|
||||||
|
printf " Signature:\t%s\n", $pci->{signature};
|
||||||
|
printf " Vendor id:\t0x%04x\n", $pci->{vendor_id};
|
||||||
|
printf " Device id:\t0x%04x\n", $pci->{device_id};
|
||||||
|
printf " Device class:\t0x%02x%02x%02x\n",
|
||||||
|
$pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf};
|
||||||
|
printf " Image length:\t0x%04x (%d)\n",
|
||||||
|
$pci->{image_length}, ( $pci->{image_length} * 512 );
|
||||||
|
printf " Runtime length:\t0x%04x (%d)\n",
|
||||||
|
$pci->{runtime_length}, ( $pci->{runtime_length} * 512 );
|
||||||
|
printf " Config header:\t0x%04x\n", $pci->{conf_header};
|
||||||
|
printf " CLP entry:\t0x%04x\n", $pci->{clp_entry};
|
||||||
|
printf "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub dispci
|
my $pnp = $rom->pnp_header();
|
||||||
{
|
if ( $pnp ) {
|
||||||
my ($pcidata) = substr($data, $pci, 0x18);
|
printf "PnP header:\n\n";
|
||||||
my ($dummy, $vendorid, $deviceid, $vpd, $pcilen, $pcirev,
|
printf " Signature:\t%s\n", $pnp->{signature};
|
||||||
$class1, $class2, $class3, $imglen, $coderev, $codetype,
|
printf " Checksum:\t0x%02x (0x%02x)\n", $pnp->{checksum}, $pnp->checksum;
|
||||||
$indicator) = unpack('a4v4C4v2C2', $pcidata);
|
printf " Manufacturer:\t0x%04x \"%s\"\n",
|
||||||
$imglen *= 512;
|
$pnp->{manufacturer}, $pnp->manufacturer;
|
||||||
my $vendorstr = sprintf('%#04x', $vendorid);
|
printf " Product:\t0x%04x \"%s\"\n", $pnp->{product}, $pnp->product;
|
||||||
my $devicestr = sprintf('%#04x', $deviceid);
|
printf " BCV:\t\t0x%04x\n", $pnp->{bcv};
|
||||||
my $coderevstr = sprintf('%#04x', $coderev);
|
printf " BDV:\t\t0x%04x\n", $pnp->{bdv};
|
||||||
print <<EOF;
|
printf " BEV:\t\t0x%04x\n", $pnp->{bev};
|
||||||
PCI structure:
|
printf "\n";
|
||||||
|
|
||||||
Vital product data: $vpd
|
|
||||||
Vendor ID: $vendorstr
|
|
||||||
Device ID: $devicestr
|
|
||||||
Device base type: $class1
|
|
||||||
Device sub type: $class2
|
|
||||||
Device interface type: $class3
|
|
||||||
Image length: $imglen
|
|
||||||
Code revision: $coderevstr
|
|
||||||
Code type: $codetype
|
|
||||||
Indicator: $indicator
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub dispnp
|
|
||||||
{
|
|
||||||
my ($pnpdata) = substr($data, $pnp, 0x20);
|
|
||||||
my ($dummy1, $pnprev, $pnplen, $nextpnp, $dummy2,
|
|
||||||
$pnpcsum, $deviceid, $mfrid, $productid,
|
|
||||||
$class1, $class2, $class3, $indicator,
|
|
||||||
$bcv, $dv, $bev, $dummy, $sri) = unpack('a4C2vC2a4v2C4v5', $pnpdata);
|
|
||||||
print <<EOF;
|
|
||||||
PnP structure:
|
|
||||||
|
|
||||||
EOF
|
|
||||||
print 'Vendor: ', &getid($mfrid), "\n";
|
|
||||||
print 'Device: ', &getid($productid), "\n";
|
|
||||||
my $indicatorstr = sprintf('%#02x', $indicator);
|
|
||||||
my $bcvstr = sprintf('%#04x', $bcv);
|
|
||||||
my $dvstr = sprintf('%#04x', $dv);
|
|
||||||
my $bevstr = sprintf('%#04x', $bev);
|
|
||||||
my $sristr = sprintf('%#04x', $sri);
|
|
||||||
my $checksum = unpack('%8C*', $pnpdata);
|
|
||||||
print <<EOF;
|
|
||||||
Device base type: $class1
|
|
||||||
Device sub type: $class2
|
|
||||||
Device interface type: $class3
|
|
||||||
Device indicator: $indicatorstr
|
|
||||||
Boot connection vector: $bcvstr
|
|
||||||
Disconnect vector: $dvstr
|
|
||||||
Bootstrap entry vector: $bevstr
|
|
||||||
Static resource information vector: $sristr
|
|
||||||
Checksum: $checksum
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pcipnp
|
|
||||||
{
|
|
||||||
($pci, $pnp) = unpack('v2', substr($data, 0x18, 4));
|
|
||||||
if ($pci >= $len or $pnp >= $len) {
|
|
||||||
print "$file: Not a PCI PnP ROM image\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (substr($data, $pci, 4) ne 'PCIR' or substr($data, $pnp, 4) ne '$PnP') {
|
|
||||||
print "$file: No PCI and PNP structures, not a PCI PNP ROM image\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
&dispci();
|
|
||||||
&dispnp();
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = $#ARGV >= 0 ? $ARGV[0] : '-';
|
|
||||||
open(F, "$file") or die "$file: $!\n";
|
|
||||||
binmode(F);
|
|
||||||
# Handle up to 64kB ROM images
|
|
||||||
$len = read(F, $data, 64*1024);
|
|
||||||
close(F);
|
|
||||||
defined($len) or die "$file: $!\n";
|
|
||||||
substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n";
|
|
||||||
my ($codelen) = unpack('C', substr($data, 2, 1));
|
|
||||||
$codelen *= 512;
|
|
||||||
if ($codelen < $len) {
|
|
||||||
my $pad = $len - $codelen;
|
|
||||||
print "Image is $codelen bytes and has $pad bytes of padding following\n";
|
|
||||||
$data = substr($data, 0, $codelen);
|
|
||||||
} elsif ($codelen > $len) {
|
|
||||||
print "Image should be $codelen bytes but is truncated to $len bytes\n";}
|
|
||||||
&pcipnp();
|
|
||||||
($csum) = unpack('%8C*', $data);
|
|
||||||
print "ROM checksum: $csum \n";
|
|
||||||
exit(0);
|
|
||||||
|
|
Loading…
Reference in New Issue