mirror of https://github.com/ipxe/ipxe.git
[util] Improve processing of ROM images in Option::ROM
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>pull/88/head
parent
956f6a7227
commit
3f4c179a14
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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 () >>
|
||||
|
@ -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;
|
||||
|
@ -250,9 +271,9 @@ 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
|
||||
|
||||
|
@ -260,9 +281,11 @@ 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};
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
|
@ -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 {
|
||||
|
@ -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 => {
|
||||
|
@ -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 {
|
||||
|
@ -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 => {
|
||||
|
@ -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;
|
||||
|
@ -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 => {
|
||||
|
@ -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;
|
||||
|
|
|
@ -28,8 +28,9 @@ my $romfile = shift || "-";
|
|||
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;
|
||||
|
||||
|
@ -38,15 +39,16 @@ do {
|
|||
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();
|
||||
|
|
Loading…
Reference in New Issue