You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ROM.pm 18KB

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