Annotation of qemu/roms/ipxe/src/util/Option/ROM.pm, revision 1.1

1.1     ! root        1: package Option::ROM;
        !             2: 
        !             3: # Copyright (C) 2008 Michael Brown <[email protected]>.
        !             4: #
        !             5: # This program is free software; you can redistribute it and/or
        !             6: # modify it under the terms of the GNU General Public License as
        !             7: # published by the Free Software Foundation; either version 2 of the
        !             8: # License, or any later version.
        !             9: #
        !            10: # This program is distributed in the hope that it will be useful, but
        !            11: # WITHOUT ANY WARRANTY; without even the implied warranty of
        !            12: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        !            13: # General Public License for more details.
        !            14: #
        !            15: # You should have received a copy of the GNU General Public License
        !            16: # along with this program; if not, write to the Free Software
        !            17: # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
        !            18: 
        !            19: =head1 NAME
        !            20: 
        !            21: Option::ROM - Option ROM manipulation
        !            22: 
        !            23: =head1 SYNOPSIS
        !            24: 
        !            25:     use Option::ROM;
        !            26: 
        !            27:     # Load a ROM image
        !            28:     my $rom = new Option::ROM;
        !            29:     $rom->load ( "rtl8139.rom" );
        !            30: 
        !            31:     # Modify the PCI device ID
        !            32:     $rom->pci_header->{device_id} = 0x1234;
        !            33:     $rom->fix_checksum();
        !            34: 
        !            35:     # Write ROM image out to a new file
        !            36:     $rom->save ( "rtl8139-modified.rom" );
        !            37: 
        !            38: =head1 DESCRIPTION
        !            39: 
        !            40: C<Option::ROM> provides a mechanism for manipulating Option ROM
        !            41: images.
        !            42: 
        !            43: =head1 METHODS
        !            44: 
        !            45: =cut
        !            46: 
        !            47: ##############################################################################
        !            48: #
        !            49: # Option::ROM::Fields
        !            50: #
        !            51: ##############################################################################
        !            52: 
        !            53: package Option::ROM::Fields;
        !            54: 
        !            55: use strict;
        !            56: use warnings;
        !            57: use Carp;
        !            58: use bytes;
        !            59: 
        !            60: sub TIEHASH {
        !            61:   my $class = shift;
        !            62:   my $self = shift;
        !            63: 
        !            64:   bless $self, $class;
        !            65:   return $self;
        !            66: }
        !            67: 
        !            68: sub FETCH {
        !            69:   my $self = shift;
        !            70:   my $key = shift;
        !            71: 
        !            72:   return undef unless $self->EXISTS ( $key );
        !            73:   my $raw = substr ( ${$self->{data}},
        !            74:                     ( $self->{offset} + $self->{fields}->{$key}->{offset} ),
        !            75:                     $self->{fields}->{$key}->{length} );
        !            76:   my $unpack = ( ref $self->{fields}->{$key}->{unpack} ?
        !            77:                 $self->{fields}->{$key}->{unpack} :
        !            78:                 sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } );
        !            79:   return &$unpack ( $raw );
        !            80: }
        !            81: 
        !            82: sub STORE {
        !            83:   my $self = shift;
        !            84:   my $key = shift;
        !            85:   my $value = shift;
        !            86: 
        !            87:   croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key );
        !            88:   my $pack = ( ref $self->{fields}->{$key}->{pack} ?
        !            89:               $self->{fields}->{$key}->{pack} :
        !            90:               sub { pack ( $self->{fields}->{$key}->{pack}, shift ); } );
        !            91:   my $raw = &$pack ( $value );
        !            92:   substr ( ${$self->{data}},
        !            93:           ( $self->{offset} + $self->{fields}->{$key}->{offset} ),
        !            94:           $self->{fields}->{$key}->{length} ) = $raw;
        !            95: }
        !            96: 
        !            97: sub DELETE {
        !            98:   my $self = shift;
        !            99:   my $key = shift;
        !           100: 
        !           101:   $self->STORE ( $key, 0 );
        !           102: }
        !           103: 
        !           104: sub CLEAR {
        !           105:   my $self = shift;
        !           106: 
        !           107:   foreach my $key ( keys %{$self->{fields}} ) {
        !           108:     $self->DELETE ( $key );
        !           109:   }
        !           110: }
        !           111: 
        !           112: sub EXISTS {
        !           113:   my $self = shift;
        !           114:   my $key = shift;
        !           115: 
        !           116:   return ( exists $self->{fields}->{$key} &&
        !           117:           ( ( $self->{fields}->{$key}->{offset} +
        !           118:               $self->{fields}->{$key}->{length} ) <= $self->{length} ) );
        !           119: }
        !           120: 
        !           121: sub FIRSTKEY {
        !           122:   my $self = shift;
        !           123: 
        !           124:   keys %{$self->{fields}};
        !           125:   return each %{$self->{fields}};
        !           126: }
        !           127: 
        !           128: sub NEXTKEY {
        !           129:   my $self = shift;
        !           130:   my $lastkey = shift;
        !           131: 
        !           132:   return each %{$self->{fields}};
        !           133: }
        !           134: 
        !           135: sub SCALAR {
        !           136:   my $self = shift;
        !           137: 
        !           138:   return 1;
        !           139: }
        !           140: 
        !           141: sub UNTIE {
        !           142:   my $self = shift;
        !           143: }
        !           144: 
        !           145: sub DESTROY {
        !           146:   my $self = shift;
        !           147: }
        !           148: 
        !           149: sub checksum {
        !           150:   my $self = shift;
        !           151: 
        !           152:   my $raw = substr ( ${$self->{data}}, $self->{offset}, $self->{length} );
        !           153:   return unpack ( "%8C*", $raw );
        !           154: }
        !           155: 
        !           156: ##############################################################################
        !           157: #
        !           158: # Option::ROM
        !           159: #
        !           160: ##############################################################################
        !           161: 
        !           162: package Option::ROM;
        !           163: 
        !           164: use strict;
        !           165: use warnings;
        !           166: use Carp;
        !           167: use bytes;
        !           168: use Exporter 'import';
        !           169: 
        !           170: use constant ROM_SIGNATURE => 0xaa55;
        !           171: use constant PCI_SIGNATURE => 'PCIR';
        !           172: use constant PNP_SIGNATURE => '$PnP';
        !           173: 
        !           174: our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PNP_SIGNATURE );
        !           175: our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
        !           176: 
        !           177: use constant JMP_SHORT => 0xeb;
        !           178: use constant JMP_NEAR => 0xe9;
        !           179: 
        !           180: sub pack_init {
        !           181:   my $dest = shift;
        !           182: 
        !           183:   # Always create a near jump; it's simpler
        !           184:   if ( $dest ) {
        !           185:     return pack ( "CS", JMP_NEAR, ( $dest - 6 ) );
        !           186:   } else {
        !           187:     return pack ( "CS", 0, 0 );
        !           188:   }
        !           189: }
        !           190: 
        !           191: sub unpack_init {
        !           192:   my $instr = shift;
        !           193: 
        !           194:   # Accept both short and near jumps
        !           195:   my $jump = unpack ( "C", $instr );
        !           196:   if ( $jump == JMP_SHORT ) {
        !           197:     my $offset = unpack ( "xC", $instr );
        !           198:     return ( $offset + 5 );
        !           199:   } elsif ( $jump == JMP_NEAR ) {
        !           200:     my $offset = unpack ( "xS", $instr );
        !           201:     return ( $offset + 6 );
        !           202:   } elsif ( $jump == 0 ) {
        !           203:     return 0;
        !           204:   } else {
        !           205:     croak "Unrecognised jump instruction in init vector\n";
        !           206:   }
        !           207: }
        !           208: 
        !           209: =pod
        !           210: 
        !           211: =item C<< new () >>
        !           212: 
        !           213: Construct a new C<Option::ROM> object.
        !           214: 
        !           215: =cut
        !           216: 
        !           217: sub new {
        !           218:   my $class = shift;
        !           219: 
        !           220:   my $hash = {};
        !           221:   tie %$hash, "Option::ROM::Fields", {
        !           222:     data => undef,
        !           223:     offset => 0x00,
        !           224:     length => 0x20,
        !           225:     fields => {
        !           226:       signature =>     { offset => 0x00, length => 0x02, pack => "S" },
        !           227:       length =>                { offset => 0x02, length => 0x01, pack => "C" },
        !           228:       # "init" is part of a jump instruction
        !           229:       init =>          { offset => 0x03, length => 0x03,
        !           230:                          pack => \&pack_init, unpack => \&unpack_init },
        !           231:       checksum =>      { offset => 0x06, length => 0x01, pack => "C" },
        !           232:       bofm_header =>   { offset => 0x14, length => 0x02, pack => "S" },
        !           233:       undi_header =>   { offset => 0x16, length => 0x02, pack => "S" },
        !           234:       pci_header =>    { offset => 0x18, length => 0x02, pack => "S" },
        !           235:       pnp_header =>    { offset => 0x1a, length => 0x02, pack => "S" },
        !           236:     },
        !           237:   };
        !           238:   bless $hash, $class;
        !           239:   return $hash;
        !           240: }
        !           241: 
        !           242: =pod
        !           243: 
        !           244: =item C<< load ( $filename ) >>
        !           245: 
        !           246: Load option ROM contents from the file C<$filename>.
        !           247: 
        !           248: =cut
        !           249: 
        !           250: sub load {
        !           251:   my $hash = shift;
        !           252:   my $self = tied(%$hash);
        !           253:   my $filename = shift;
        !           254: 
        !           255:   $self->{filename} = $filename;
        !           256: 
        !           257:   open my $fh, "<$filename"
        !           258:       or croak "Cannot open $filename for reading: $!";
        !           259:   read $fh, my $data, ( 128 * 1024 ); # 128kB is theoretical max size
        !           260:   $self->{data} = \$data;
        !           261:   close $fh;
        !           262: }
        !           263: 
        !           264: =pod
        !           265: 
        !           266: =item C<< save ( [ $filename ] ) >>
        !           267: 
        !           268: Write the ROM data back out to the file C<$filename>.  If C<$filename>
        !           269: is omitted, the file used in the call to C<load()> will be used.
        !           270: 
        !           271: =cut
        !           272: 
        !           273: sub save {
        !           274:   my $hash = shift;
        !           275:   my $self = tied(%$hash);
        !           276:   my $filename = shift;
        !           277: 
        !           278:   $filename ||= $self->{filename};
        !           279: 
        !           280:   open my $fh, ">$filename"
        !           281:       or croak "Cannot open $filename for writing: $!";
        !           282:   print $fh ${$self->{data}};
        !           283:   close $fh;
        !           284: }
        !           285: 
        !           286: =pod
        !           287: 
        !           288: =item C<< length () >>
        !           289: 
        !           290: Length of option ROM data.  This is the length of the file, not the
        !           291: length from the ROM header length field.
        !           292: 
        !           293: =cut
        !           294: 
        !           295: sub length {
        !           296:   my $hash = shift;
        !           297:   my $self = tied(%$hash);
        !           298: 
        !           299:   return length ${$self->{data}};
        !           300: }
        !           301: 
        !           302: =pod
        !           303: 
        !           304: =item C<< pci_header () >>
        !           305: 
        !           306: Return a C<Option::ROM::PCI> object representing the ROM's PCI header,
        !           307: if present.
        !           308: 
        !           309: =cut
        !           310: 
        !           311: sub pci_header {
        !           312:   my $hash = shift;
        !           313:   my $self = tied(%$hash);
        !           314: 
        !           315:   my $offset = $hash->{pci_header};
        !           316:   return undef unless $offset != 0;
        !           317: 
        !           318:   return Option::ROM::PCI->new ( $self->{data}, $offset );
        !           319: }
        !           320: 
        !           321: =pod
        !           322: 
        !           323: =item C<< pnp_header () >>
        !           324: 
        !           325: Return a C<Option::ROM::PnP> object representing the ROM's PnP header,
        !           326: if present.
        !           327: 
        !           328: =cut
        !           329: 
        !           330: sub pnp_header {
        !           331:   my $hash = shift;
        !           332:   my $self = tied(%$hash);
        !           333: 
        !           334:   my $offset = $hash->{pnp_header};
        !           335:   return undef unless $offset != 0;
        !           336: 
        !           337:   return Option::ROM::PnP->new ( $self->{data}, $offset );
        !           338: }
        !           339: 
        !           340: =pod
        !           341: 
        !           342: =item C<< checksum () >>
        !           343: 
        !           344: Calculate the byte checksum of the ROM.
        !           345: 
        !           346: =cut
        !           347: 
        !           348: sub checksum {
        !           349:   my $hash = shift;
        !           350:   my $self = tied(%$hash);
        !           351: 
        !           352:   my $raw = substr ( ${$self->{data}}, 0, ( $hash->{length} * 512 ) );
        !           353:   return unpack ( "%8C*", $raw );
        !           354: }
        !           355: 
        !           356: =pod
        !           357: 
        !           358: =item C<< fix_checksum () >>
        !           359: 
        !           360: Fix the byte checksum of the ROM.
        !           361: 
        !           362: =cut
        !           363: 
        !           364: sub fix_checksum {
        !           365:   my $hash = shift;
        !           366:   my $self = tied(%$hash);
        !           367: 
        !           368:   $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
        !           369: }
        !           370: 
        !           371: ##############################################################################
        !           372: #
        !           373: # Option::ROM::PCI
        !           374: #
        !           375: ##############################################################################
        !           376: 
        !           377: package Option::ROM::PCI;
        !           378: 
        !           379: use strict;
        !           380: use warnings;
        !           381: use Carp;
        !           382: use bytes;
        !           383: 
        !           384: sub new {
        !           385:   my $class = shift;
        !           386:   my $data = shift;
        !           387:   my $offset = shift;
        !           388: 
        !           389:   my $hash = {};
        !           390:   tie %$hash, "Option::ROM::Fields", {
        !           391:     data => $data,
        !           392:     offset => $offset,
        !           393:     length => 0x0c,
        !           394:     fields => {
        !           395:       signature =>     { offset => 0x00, length => 0x04, pack => "a4" },
        !           396:       vendor_id =>     { offset => 0x04, length => 0x02, pack => "S" },
        !           397:       device_id =>     { offset => 0x06, length => 0x02, pack => "S" },
        !           398:       device_list =>   { offset => 0x08, length => 0x02, pack => "S" },
        !           399:       struct_length => { offset => 0x0a, length => 0x02, pack => "S" },
        !           400:       struct_revision =>{ offset => 0x0c, length => 0x01, pack => "C" },
        !           401:       base_class =>    { offset => 0x0d, length => 0x01, pack => "C" },
        !           402:       sub_class =>     { offset => 0x0e, length => 0x01, pack => "C" },
        !           403:       prog_intf =>     { offset => 0x0f, length => 0x01, pack => "C" },
        !           404:       image_length =>  { offset => 0x10, length => 0x02, pack => "S" },
        !           405:       revision =>      { offset => 0x12, length => 0x02, pack => "S" },
        !           406:       code_type =>     { offset => 0x14, length => 0x01, pack => "C" },
        !           407:       last_image =>    { offset => 0x15, length => 0x01, pack => "C" },
        !           408:       runtime_length =>        { offset => 0x16, length => 0x02, pack => "S" },
        !           409:       conf_header =>   { offset => 0x18, length => 0x02, pack => "S" },
        !           410:       clp_entry =>     { offset => 0x1a, length => 0x02, pack => "S" },
        !           411:     },
        !           412:   };
        !           413:   bless $hash, $class;
        !           414: 
        !           415:   # Retrieve true length of structure
        !           416:   my $self = tied ( %$hash );
        !           417:   $self->{length} = $hash->{struct_length};
        !           418: 
        !           419:   return $hash;  
        !           420: }
        !           421: 
        !           422: ##############################################################################
        !           423: #
        !           424: # Option::ROM::PnP
        !           425: #
        !           426: ##############################################################################
        !           427: 
        !           428: package Option::ROM::PnP;
        !           429: 
        !           430: use strict;
        !           431: use warnings;
        !           432: use Carp;
        !           433: use bytes;
        !           434: 
        !           435: sub new {
        !           436:   my $class = shift;
        !           437:   my $data = shift;
        !           438:   my $offset = shift;
        !           439: 
        !           440:   my $hash = {};
        !           441:   tie %$hash, "Option::ROM::Fields", {
        !           442:     data => $data,
        !           443:     offset => $offset,
        !           444:     length => 0x06,
        !           445:     fields => {
        !           446:       signature =>     { offset => 0x00, length => 0x04, pack => "a4" },
        !           447:       struct_revision =>{ offset => 0x04, length => 0x01, pack => "C" },
        !           448:       struct_length => { offset => 0x05, length => 0x01, pack => "C" },
        !           449:       checksum =>      { offset => 0x09, length => 0x01, pack => "C" },
        !           450:       manufacturer =>  { offset => 0x0e, length => 0x02, pack => "S" },
        !           451:       product =>       { offset => 0x10, length => 0x02, pack => "S" },
        !           452:       bcv =>           { offset => 0x16, length => 0x02, pack => "S" },
        !           453:       bdv =>           { offset => 0x18, length => 0x02, pack => "S" },
        !           454:       bev =>           { offset => 0x1a, length => 0x02, pack => "S" },
        !           455:     },
        !           456:   };
        !           457:   bless $hash, $class;
        !           458: 
        !           459:   # Retrieve true length of structure
        !           460:   my $self = tied ( %$hash );
        !           461:   $self->{length} = ( $hash->{struct_length} * 16 );
        !           462: 
        !           463:   return $hash;  
        !           464: }
        !           465: 
        !           466: sub checksum {
        !           467:   my $hash = shift;
        !           468:   my $self = tied(%$hash);
        !           469: 
        !           470:   return $self->checksum();
        !           471: }
        !           472: 
        !           473: sub fix_checksum {
        !           474:   my $hash = shift;
        !           475:   my $self = tied(%$hash);
        !           476: 
        !           477:   $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
        !           478: }
        !           479: 
        !           480: sub manufacturer {
        !           481:   my $hash = shift;
        !           482:   my $self = tied(%$hash);
        !           483: 
        !           484:   my $manufacturer = $hash->{manufacturer};
        !           485:   return undef unless $manufacturer;
        !           486: 
        !           487:   my $raw = substr ( ${$self->{data}}, $manufacturer );
        !           488:   return unpack ( "Z*", $raw );
        !           489: }
        !           490: 
        !           491: sub product {
        !           492:   my $hash = shift;
        !           493:   my $self = tied(%$hash);
        !           494: 
        !           495:   my $product = $hash->{product};
        !           496:   return undef unless $product;
        !           497: 
        !           498:   my $raw = substr ( ${$self->{data}}, $product );
        !           499:   return unpack ( "Z*", $raw );
        !           500: }
        !           501: 
        !           502: 1;

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.