Skip to content

Commit

Permalink
[util] Improve processing of ROM images in Option::ROM
Browse files Browse the repository at this point in the history
The Option::ROM module now compares the Code Type in the PCIR header
to 0x00 (PC-AT) in order to check the presence of other header types
(PnP, UNDI, iPXE, etc).  The validity of these headers are checked not
only by offset, but by range and signature checks also.  The image
checksum and initial size also depends on Code Type.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
pe-bo authored and mcb30 committed Jan 21, 2019
1 parent 956f6a7 commit 3f4c179
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 39 deletions.
137 changes: 103 additions & 34 deletions src/util/Option/ROM.pm
Expand Up @@ -116,7 +116,9 @@ sub EXISTS {

return ( exists $self->{fields}->{$key} &&
( ( $self->{fields}->{$key}->{offset} +
$self->{fields}->{$key}->{length} ) <= $self->{length} ) );
$self->{fields}->{$key}->{length} ) <= $self->{length} ) &&
( ! defined $self->{fields}->{$key}->{check} ||
&{$self->{fields}->{$key}->{check}} ( $self, $key ) ) );
}

sub FIRSTKEY {
Expand Down Expand Up @@ -172,10 +174,11 @@ use constant ROM_SIGNATURE => 0xaa55;
use constant PCI_SIGNATURE => 'PCIR';
use constant PCI_LAST_IMAGE => 0x80;
use constant PNP_SIGNATURE => '$PnP';
use constant UNDI_SIGNATURE => 'UNDI';
use constant IPXE_SIGNATURE => 'iPXE';

our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
PNP_SIGNATURE IPXE_SIGNATURE );
PNP_SIGNATURE UNDI_SIGNATURE IPXE_SIGNATURE );
our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );

use constant JMP_SHORT => 0xeb;
Expand Down Expand Up @@ -210,10 +213,20 @@ sub unpack_init {
} elsif ( $jump == 0 ) {
return 0;
} else {
croak "Unrecognised jump instruction in init vector\n";
carp "Unrecognised jump instruction in init vector\n";
return 0;
}
}

sub check_pcat_rom {
my $self = shift;
my $key = shift;

my $pci = $self->{rom}->pci_header ();

return ! defined $pci || $pci->{code_type} == 0x00;
}

=pod
=item C<< new () >>
Expand All @@ -227,21 +240,29 @@ sub new {

my $hash = {};
tie %$hash, "Option::ROM::Fields", {
rom => $hash, # ROM object itself
data => undef,
offset => 0x00,
length => 0x20,
file_offset => 0x0,
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" },
pack => \&pack_init, unpack => \&unpack_init,
check => \&check_pcat_rom },
checksum => { offset => 0x06, length => 0x01, pack => "C",
check => \&check_pcat_rom },
ipxe_header => { offset => 0x10, length => 0x02, pack => "S",
check => \&check_pcat_rom },
bofm_header => { offset => 0x14, length => 0x02, pack => "S",
check => \&check_pcat_rom },
undi_header => { offset => 0x16, length => 0x02, pack => "S",
check => \&check_pcat_rom },
pci_header => { offset => 0x18, length => 0x02, pack => "S" },
pnp_header => { offset => 0x1a, length => 0x02, pack => "S" },
pnp_header => { offset => 0x1a, length => 0x02, pack => "S",
check => \&check_pcat_rom },
},
};
bless $hash, $class;
Expand All @@ -250,19 +271,21 @@ sub new {

=pod
=item C<< set ( $data ) >>
=item C<< set ( $data [, $file_offset ] ) >>
Set option ROM contents.
Set option ROM contents, optionally sets original file offset.
=cut

sub set {
my $hash = shift;
my $self = tied(%$hash);
my $data = shift;
my $file_offset = shift // 0x0;

# Store data
$self->{data} = \$data;
$self->{file_offset} = $file_offset;

# Split out any data belonging to the next image
delete $self->{next_image};
Expand All @@ -273,7 +296,7 @@ sub set {
my $remainder = substr ( $data, $length );
$data = substr ( $data, 0, $length );
$self->{next_image} = new Option::ROM;
$self->{next_image}->set ( $remainder );
$self->{next_image}->set ( $remainder, $self->{file_offset} + $length );
}
}

Expand Down Expand Up @@ -311,6 +334,7 @@ sub load {

open my $fh, "<$filename"
or croak "Cannot open $filename for reading: $!";
binmode $fh;
read $fh, my $data, -s $fh;
$hash->set ( $data );
close $fh;
Expand All @@ -335,6 +359,7 @@ sub save {
open my $fh, ">$filename"
or croak "Cannot open $filename for writing: $!";
my $data = $hash->get();
binmode $fh;
print $fh $data;
close $fh;
}
Expand Down Expand Up @@ -369,9 +394,9 @@ sub pci_header {
my $self = tied(%$hash);

my $offset = $hash->{pci_header};
return undef unless $offset != 0;
return undef unless $offset;

return Option::ROM::PCI->new ( $self->{data}, $offset );
return Option::ROM::PCI->new ( $self, $offset );
}

=pod
Expand All @@ -388,9 +413,9 @@ sub pnp_header {
my $self = tied(%$hash);

my $offset = $hash->{pnp_header};
return undef unless $offset != 0;
return undef unless $offset;

return Option::ROM::PnP->new ( $self->{data}, $offset );
return Option::ROM::PnP->new ( $self, $offset );
}

=pod
Expand All @@ -407,9 +432,9 @@ sub undi_header {
my $self = tied(%$hash);

my $offset = $hash->{undi_header};
return undef unless $offset != 0;
return undef unless $offset;

return Option::ROM::UNDI->new ( $self->{data}, $offset );
return Option::ROM::UNDI->new ( $self, $offset );
}

=pod
Expand All @@ -426,9 +451,9 @@ sub ipxe_header {
my $self = tied(%$hash);

my $offset = $hash->{ipxe_header};
return undef unless $offset != 0;
return undef unless $offset;

return Option::ROM::iPXE->new ( $self->{data}, $offset );
return Option::ROM::iPXE->new ( $self, $offset );
}

=pod
Expand Down Expand Up @@ -475,9 +500,25 @@ sub fix_checksum {
my $hash = shift;
my $self = tied(%$hash);

return unless ( exists $hash->{checksum} );
$hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
}

=pod
=item C<< file_offset () >>
Get file offset of image.
=cut

sub file_offset {
my $hash = shift;
my $self = tied(%$hash);

return $self->{file_offset};
}

##############################################################################
#
# Option::ROM::PCI
Expand All @@ -493,12 +534,13 @@ use bytes;

sub new {
my $class = shift;
my $data = shift;
my $rom = shift;
my $offset = shift;

my $hash = {};
tie %$hash, "Option::ROM::Fields", {
data => $data,
rom => $rom,
data => $rom->{data},
offset => $offset,
length => 0x0c,
fields => {
Expand All @@ -522,11 +564,17 @@ sub new {
};
bless $hash, $class;

# Retrieve true length of structure
my $self = tied ( %$hash );
my $length = $rom->{rom}->length ();

return undef unless ( $offset + $self->{length} <= $length &&
$hash->{signature} eq Option::ROM::PCI_SIGNATURE &&
$offset + $hash->{struct_length} <= $length );

# Retrieve true length of structure
$self->{length} = $hash->{struct_length};

return $hash;
return $hash;
}

sub device_list {
Expand Down Expand Up @@ -564,12 +612,13 @@ use bytes;

sub new {
my $class = shift;
my $data = shift;
my $rom = shift;
my $offset = shift;

my $hash = {};
tie %$hash, "Option::ROM::Fields", {
data => $data,
rom => $rom,
data => $rom->{data},
offset => $offset,
length => 0x06,
fields => {
Expand All @@ -586,11 +635,17 @@ sub new {
};
bless $hash, $class;

# Retrieve true length of structure
my $self = tied ( %$hash );
my $length = $rom->{rom}->length ();

return undef unless ( $offset + $self->{length} <= $length &&
$hash->{signature} eq Option::ROM::PNP_SIGNATURE &&
$offset + $hash->{struct_length} * 16 <= $length );

# Retrieve true length of structure
$self->{length} = ( $hash->{struct_length} * 16 );

return $hash;
return $hash;
}

sub checksum {
Expand Down Expand Up @@ -644,12 +699,13 @@ use bytes;

sub new {
my $class = shift;
my $data = shift;
my $rom = shift;
my $offset = shift;

my $hash = {};
tie %$hash, "Option::ROM::Fields", {
data => $data,
rom => $rom,
data => $rom->{data},
offset => $offset,
length => 0x16,
fields => {
Expand All @@ -669,8 +725,14 @@ sub new {
};
bless $hash, $class;

# Retrieve true length of structure
my $self = tied ( %$hash );
my $length = $rom->{rom}->length ();

return undef unless ( $offset + $self->{length} <= $length &&
$hash->{signature} eq Option::ROM::UNDI_SIGNATURE &&
$offset + $hash->{struct_length} <= $length );

# Retrieve true length of structure
$self->{length} = $hash->{struct_length};

return $hash;
Expand Down Expand Up @@ -705,12 +767,13 @@ use bytes;

sub new {
my $class = shift;
my $data = shift;
my $rom = shift;
my $offset = shift;

my $hash = {};
tie %$hash, "Option::ROM::Fields", {
data => $data,
rom => $rom,
data => $rom->{data},
offset => $offset,
length => 0x06,
fields => {
Expand All @@ -723,8 +786,14 @@ sub new {
};
bless $hash, $class;

# Retrieve true length of structure
my $self = tied ( %$hash );
my $length = $rom->{rom}->length ();

return undef unless ( $offset + $self->{length} <= $length &&
$hash->{signature} eq Option::ROM::IPXE_SIGNATURE &&
$offset + $hash->{struct_length} <= $length );

# Retrieve true length of structure
$self->{length} = $hash->{struct_length};

return $hash;
Expand Down
12 changes: 7 additions & 5 deletions src/util/disrom.pl
Expand Up @@ -28,8 +28,9 @@
my $rom = new Option::ROM;
$rom->load ( $romfile );

do {
my $index = 0;

do {
die "Not an option ROM image\n"
unless $rom->{signature} == ROM_SIGNATURE;

Expand All @@ -38,15 +39,16 @@
die "ROM image truncated (is $filelength, should be $romlength)\n"
if $filelength < $romlength;

printf "Index: %d, offset: 0x%08x\n\n", $index++, $rom->file_offset;
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};
( ( $rom->checksum () == 0 ) ? "" : "INCORRECT: " ), $rom->checksum () if ( exists $rom->{checksum} );
printf " %-16s 0x%04x\n", "Init:", $rom->{init} if ( defined $rom->{init} );
printf " %-16s 0x%04x\n", "UNDI header:", $rom->{undi_header} if ( exists $rom->{undi_header} );
printf " %-16s 0x%04x\n", "PCI header:", $rom->{pci_header};
printf " %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header};
printf " %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header} if ( exists $rom->{pnp_header} );
printf "\n";

my $pci = $rom->pci_header();
Expand Down

0 comments on commit 3f4c179

Please sign in to comment.