summaryrefslogtreecommitdiff
path: root/roms/ipxe/src/util/Option/ROM.pm
diff options
context:
space:
mode:
Diffstat (limited to 'roms/ipxe/src/util/Option/ROM.pm')
-rw-r--r--roms/ipxe/src/util/Option/ROM.pm236
1 files changed, 231 insertions, 5 deletions
diff --git a/roms/ipxe/src/util/Option/ROM.pm b/roms/ipxe/src/util/Option/ROM.pm
index 9fea4d344..fb37ce4b0 100644
--- a/roms/ipxe/src/util/Option/ROM.pm
+++ b/roms/ipxe/src/util/Option/ROM.pm
@@ -14,7 +14,8 @@ package Option::ROM;
#
# 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
=head1 NAME
@@ -169,13 +170,17 @@ 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 PNP_SIGNATURE );
+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;
@@ -199,6 +204,9 @@ sub unpack_init {
} 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 {
@@ -229,6 +237,7 @@ sub new {
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" },
@@ -241,6 +250,53 @@ sub new {
=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 $length = ( $hash->{length} * 512 );
+ my $pci_header = $hash->pci_header();
+ if ( ( $length < length $data ) &&
+ ( defined $pci_header ) &&
+ ( ! ( $pci_header->{last_image} & PCI_LAST_IMAGE ) ) ) {
+ 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>.
@@ -256,8 +312,8 @@ sub load {
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;
+ read $fh, my $data, -s $fh;
+ $hash->set ( $data );
close $fh;
}
@@ -279,7 +335,8 @@ sub save {
open my $fh, ">$filename"
or croak "Cannot open $filename for writing: $!";
- print $fh ${$self->{data}};
+ my $data = $hash->get();
+ print $fh $data;
close $fh;
}
@@ -339,6 +396,60 @@ sub pnp_header {
=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.
@@ -499,4 +610,119 @@ sub 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;