diff --git a/src/util/Option/ROM.pm b/src/util/Option/ROM.pm index f5c33f8a..7a1bb883 100644 --- a/src/util/Option/ROM.pm +++ b/src/util/Option/ROM.pm @@ -73,7 +73,10 @@ sub FETCH { my $raw = substr ( ${$self->{data}}, ( $self->{offset} + $self->{fields}->{$key}->{offset} ), $self->{fields}->{$key}->{length} ); - return unpack ( $self->{fields}->{$key}->{pack}, $raw ); + my $unpack = ( ref $self->{fields}->{$key}->{unpack} ? + $self->{fields}->{$key}->{unpack} : + sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } ); + return &$unpack ( $raw ); } sub STORE { @@ -82,7 +85,10 @@ sub STORE { my $value = shift; croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key ); - my $raw = pack ( $self->{fields}->{$key}->{pack}, $value ); + 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; @@ -168,6 +174,36 @@ use constant PNP_SIGNATURE => '$PnP'; our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PNP_SIGNATURE ); our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] ); +use constant JMP_SHORT => 0xeb; +use constant JMP_NEAR => 0xe9; + +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, my $offset ) = unpack ( "CS", $instr ); + if ( $jump == JMP_SHORT ) { + return ( $offset + 5 ); + } elsif ( $jump == JMP_NEAR ) { + return ( $offset + 6 ); + } elsif ( $jump == 0 ) { + return 0; + } else { + croak "Unrecognised jump instruction in init vector\n"; + } +} + =pod =item C<< new () >> @@ -187,7 +223,11 @@ sub new { 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" }, + 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" }, diff --git a/src/util/disrom.pl b/src/util/disrom.pl index c472037a..80ac4af8 100755 --- a/src/util/disrom.pl +++ b/src/util/disrom.pl @@ -38,6 +38,7 @@ die "ROM image truncated (is $filelength, should be $romlength)\n" 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 " Init:\t\t0x%04x\n", $rom->{init}; 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}; diff --git a/src/util/mergerom.pl b/src/util/mergerom.pl index ce1befb7..f9c52502 100755 --- a/src/util/mergerom.pl +++ b/src/util/mergerom.pl @@ -23,6 +23,18 @@ 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; @@ -34,6 +46,12 @@ foreach my $rom ( @roms ) { # Update base length $baserom->{length} += $rom->{length}; + # 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; @@ -52,8 +70,8 @@ foreach my $rom ( @roms ) { # Merge CLP entry point if ( exists ( $baserom_pci->{clp_entry} ) && exists ( $rom_pci->{clp_entry} ) ) { - $baserom_pci->{clp_entry} = ( $offset + $rom_pci->{clp_entry} ) - if $rom_pci->{clp_entry}; + merge_entry_points ( $baserom_pci->{clp_entry}, $rom_pci->{clp_entry}, + $offset ); } } @@ -61,9 +79,9 @@ foreach my $rom ( @roms ) { my $baserom_pnp = $baserom->pnp_header; my $rom_pnp = $rom->pnp_header; if ( $baserom_pnp && $rom_pnp ) { - $baserom_pnp->{bcv} = ( $offset + $rom_pnp->{bcv} ) if $rom_pnp->{bcv}; - $baserom_pnp->{bdv} = ( $offset + $rom_pnp->{bdv} ) if $rom_pnp->{bdv}; - $baserom_pnp->{bev} = ( $offset + $rom_pnp->{bev} ) if $rom_pnp->{bev}; + 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 ); } # Fix checksum for this ROM segment