|
|
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;
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.