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 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  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. use constant EFI_SIGNATURE => 0x00000ef1;
  139. our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
  140. PNP_SIGNATURE UNDI_SIGNATURE IPXE_SIGNATURE EFI_SIGNATURE );
  141. our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
  142. use constant JMP_SHORT => 0xeb;
  143. use constant JMP_NEAR => 0xe9;
  144. use constant CALL_NEAR => 0xe8;
  145. sub pack_init {
  146. my $dest = shift;
  147. # Always create a near jump; it's simpler
  148. if ( $dest ) {
  149. return pack ( "CS", JMP_NEAR, ( $dest - 6 ) );
  150. } else {
  151. return pack ( "CS", 0, 0 );
  152. }
  153. }
  154. sub unpack_init {
  155. my $instr = shift;
  156. # Accept both short and near jumps
  157. my $jump = unpack ( "C", $instr );
  158. if ( $jump == JMP_SHORT ) {
  159. my $offset = unpack ( "xC", $instr );
  160. return ( $offset + 5 );
  161. } elsif ( $jump == JMP_NEAR ) {
  162. my $offset = unpack ( "xS", $instr );
  163. return ( $offset + 6 );
  164. } elsif ( $jump == CALL_NEAR ) {
  165. my $offset = unpack ( "xS", $instr );
  166. return ( $offset + 6 );
  167. } elsif ( $jump == 0 ) {
  168. return 0;
  169. } else {
  170. carp "Unrecognised jump instruction in init vector\n";
  171. return 0;
  172. }
  173. }
  174. sub check_pcat_rom {
  175. my $self = shift;
  176. my $key = shift;
  177. my $pci = $self->{rom}->pci_header ();
  178. return ! defined $pci || $pci->{code_type} == 0x00;
  179. }
  180. =pod
  181. =item C<< new () >>
  182. Construct a new C<Option::ROM> object.
  183. =cut
  184. sub new {
  185. my $class = shift;
  186. my $hash = {};
  187. tie %$hash, "Option::ROM::Fields", {
  188. rom => $hash, # ROM object itself
  189. data => undef,
  190. offset => 0x00,
  191. length => 0x20,
  192. file_offset => 0x0,
  193. fields => {
  194. signature => { offset => 0x00, length => 0x02, pack => "S" },
  195. length => { offset => 0x02, length => 0x01, pack => "C" },
  196. # "init" is part of a jump instruction
  197. init => { offset => 0x03, length => 0x03,
  198. pack => \&pack_init, unpack => \&unpack_init,
  199. check => \&check_pcat_rom },
  200. checksum => { offset => 0x06, length => 0x01, pack => "C",
  201. check => \&check_pcat_rom },
  202. ipxe_header => { offset => 0x10, length => 0x02, pack => "S",
  203. check => \&check_pcat_rom },
  204. bofm_header => { offset => 0x14, length => 0x02, pack => "S",
  205. check => \&check_pcat_rom },
  206. undi_header => { offset => 0x16, length => 0x02, pack => "S",
  207. check => \&check_pcat_rom },
  208. pci_header => { offset => 0x18, length => 0x02, pack => "S" },
  209. pnp_header => { offset => 0x1a, length => 0x02, pack => "S",
  210. check => \&check_pcat_rom },
  211. },
  212. };
  213. bless $hash, $class;
  214. return $hash;
  215. }
  216. =pod
  217. =item C<< set ( $data [, $file_offset ] ) >>
  218. Set option ROM contents, optionally sets original file offset.
  219. =cut
  220. sub set {
  221. my $hash = shift;
  222. my $self = tied(%$hash);
  223. my $data = shift;
  224. my $file_offset = shift // 0x0;
  225. # Store data
  226. $self->{data} = \$data;
  227. $self->{file_offset} = $file_offset;
  228. # Split out any data belonging to the next image
  229. delete $self->{next_image};
  230. my $pci_header = $hash->pci_header();
  231. if ( ( defined $pci_header ) &&
  232. ( ! ( $pci_header->{last_image} & PCI_LAST_IMAGE ) ) ) {
  233. my $length = ( $pci_header->{image_length} * 512 );
  234. my $remainder = substr ( $data, $length );
  235. $data = substr ( $data, 0, $length );
  236. $self->{next_image} = new Option::ROM;
  237. $self->{next_image}->set ( $remainder, $self->{file_offset} + $length );
  238. }
  239. }
  240. =pod
  241. =item C<< get () >>
  242. Get option ROM contents.
  243. =cut
  244. sub get {
  245. my $hash = shift;
  246. my $self = tied(%$hash);
  247. my $data = ${$self->{data}};
  248. $data .= $self->{next_image}->get() if $self->{next_image};
  249. return $data;
  250. }
  251. =pod
  252. =item C<< load ( $filename ) >>
  253. Load option ROM contents from the file C<$filename>.
  254. =cut
  255. sub load {
  256. my $hash = shift;
  257. my $self = tied(%$hash);
  258. my $filename = shift;
  259. $self->{filename} = $filename;
  260. open my $fh, "<$filename"
  261. or croak "Cannot open $filename for reading: $!";
  262. binmode $fh;
  263. read $fh, my $data, -s $fh;
  264. $hash->set ( $data );
  265. close $fh;
  266. }
  267. =pod
  268. =item C<< save ( [ $filename ] ) >>
  269. Write the ROM data back out to the file C<$filename>. If C<$filename>
  270. is omitted, the file used in the call to C<load()> will be used.
  271. =cut
  272. sub save {
  273. my $hash = shift;
  274. my $self = tied(%$hash);
  275. my $filename = shift;
  276. $filename ||= $self->{filename};
  277. open my $fh, ">$filename"
  278. or croak "Cannot open $filename for writing: $!";
  279. my $data = $hash->get();
  280. binmode $fh;
  281. print $fh $data;
  282. close $fh;
  283. }
  284. =pod
  285. =item C<< length () >>
  286. Length of option ROM data. This is the length of the file, not the
  287. length from the ROM header length field.
  288. =cut
  289. sub length {
  290. my $hash = shift;
  291. my $self = tied(%$hash);
  292. return length ${$self->{data}};
  293. }
  294. =pod
  295. =item C<< pci_header () >>
  296. Return a C<Option::ROM::PCI> object representing the ROM's PCI header,
  297. if present.
  298. =cut
  299. sub pci_header {
  300. my $hash = shift;
  301. my $self = tied(%$hash);
  302. my $offset = $hash->{pci_header};
  303. return undef unless $offset;
  304. return Option::ROM::PCI->new ( $self, $offset );
  305. }
  306. =pod
  307. =item C<< pnp_header () >>
  308. Return a C<Option::ROM::PnP> object representing the ROM's PnP header,
  309. if present.
  310. =cut
  311. sub pnp_header {
  312. my $hash = shift;
  313. my $self = tied(%$hash);
  314. my $offset = $hash->{pnp_header};
  315. return undef unless $offset;
  316. return Option::ROM::PnP->new ( $self, $offset );
  317. }
  318. =pod
  319. =item C<< undi_header () >>
  320. Return a C<Option::ROM::UNDI> object representing the ROM's UNDI header,
  321. if present.
  322. =cut
  323. sub undi_header {
  324. my $hash = shift;
  325. my $self = tied(%$hash);
  326. my $offset = $hash->{undi_header};
  327. return undef unless $offset;
  328. return Option::ROM::UNDI->new ( $self, $offset );
  329. }
  330. =pod
  331. =item C<< ipxe_header () >>
  332. Return a C<Option::ROM::iPXE> object representing the ROM's iPXE
  333. header, if present.
  334. =cut
  335. sub ipxe_header {
  336. my $hash = shift;
  337. my $self = tied(%$hash);
  338. my $offset = $hash->{ipxe_header};
  339. return undef unless $offset;
  340. return Option::ROM::iPXE->new ( $self, $offset );
  341. }
  342. =pod
  343. =item C<< efi_header () >>
  344. Return a C<Option::ROM::EFI> object representing the ROM's EFI header,
  345. if present.
  346. =cut
  347. sub efi_header {
  348. my $hash = shift;
  349. my $self = tied(%$hash);
  350. my $pci = $hash->pci_header ();
  351. return undef unless defined $pci;
  352. return Option::ROM::EFI->new ( $self, $pci );
  353. }
  354. =pod
  355. =item C<< next_image () >>
  356. Return a C<Option::ROM> object representing the next image within the
  357. ROM, if present.
  358. =cut
  359. sub next_image {
  360. my $hash = shift;
  361. my $self = tied(%$hash);
  362. return $self->{next_image};
  363. }
  364. =pod
  365. =item C<< checksum () >>
  366. Calculate the byte checksum of the ROM.
  367. =cut
  368. sub checksum {
  369. my $hash = shift;
  370. my $self = tied(%$hash);
  371. my $raw = substr ( ${$self->{data}}, 0, ( $hash->{length} * 512 ) );
  372. return unpack ( "%8C*", $raw );
  373. }
  374. =pod
  375. =item C<< fix_checksum () >>
  376. Fix the byte checksum of the ROM.
  377. =cut
  378. sub fix_checksum {
  379. my $hash = shift;
  380. my $self = tied(%$hash);
  381. return unless ( exists $hash->{checksum} );
  382. $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
  383. }
  384. =pod
  385. =item C<< file_offset () >>
  386. Get file offset of image.
  387. =cut
  388. sub file_offset {
  389. my $hash = shift;
  390. my $self = tied(%$hash);
  391. return $self->{file_offset};
  392. }
  393. ##############################################################################
  394. #
  395. # Option::ROM::PCI
  396. #
  397. ##############################################################################
  398. package Option::ROM::PCI;
  399. use strict;
  400. use warnings;
  401. use Carp;
  402. use bytes;
  403. sub new {
  404. my $class = shift;
  405. my $rom = shift;
  406. my $offset = shift;
  407. my $hash = {};
  408. tie %$hash, "Option::ROM::Fields", {
  409. rom => $rom,
  410. data => $rom->{data},
  411. offset => $offset,
  412. length => 0x0c,
  413. fields => {
  414. signature => { offset => 0x00, length => 0x04, pack => "a4" },
  415. vendor_id => { offset => 0x04, length => 0x02, pack => "S" },
  416. device_id => { offset => 0x06, length => 0x02, pack => "S" },
  417. device_list => { offset => 0x08, length => 0x02, pack => "S" },
  418. struct_length => { offset => 0x0a, length => 0x02, pack => "S" },
  419. struct_revision =>{ offset => 0x0c, length => 0x01, pack => "C" },
  420. prog_intf => { offset => 0x0d, length => 0x01, pack => "C" },
  421. sub_class => { offset => 0x0e, length => 0x01, pack => "C" },
  422. base_class => { offset => 0x0f, length => 0x01, pack => "C" },
  423. image_length => { offset => 0x10, length => 0x02, pack => "S" },
  424. revision => { offset => 0x12, length => 0x02, pack => "S" },
  425. code_type => { offset => 0x14, length => 0x01, pack => "C" },
  426. last_image => { offset => 0x15, length => 0x01, pack => "C" },
  427. runtime_length => { offset => 0x16, length => 0x02, pack => "S" },
  428. conf_header => { offset => 0x18, length => 0x02, pack => "S" },
  429. clp_entry => { offset => 0x1a, length => 0x02, pack => "S" },
  430. },
  431. };
  432. bless $hash, $class;
  433. my $self = tied ( %$hash );
  434. my $length = $rom->{rom}->length ();
  435. return undef unless ( $offset + $self->{length} <= $length &&
  436. $hash->{signature} eq Option::ROM::PCI_SIGNATURE &&
  437. $offset + $hash->{struct_length} <= $length );
  438. # Retrieve true length of structure
  439. $self->{length} = $hash->{struct_length};
  440. return $hash;
  441. }
  442. sub device_list {
  443. my $hash = shift;
  444. my $self = tied(%$hash);
  445. my $device_list = $hash->{device_list};
  446. return undef unless $device_list;
  447. my @ids;
  448. my $offset = ( $self->{offset} + $device_list );
  449. while ( 1 ) {
  450. my $raw = substr ( ${$self->{data}}, $offset, 2 );
  451. my $id = unpack ( "S", $raw );
  452. last unless $id;
  453. push @ids, $id;
  454. $offset += 2;
  455. }
  456. return @ids;
  457. }
  458. ##############################################################################
  459. #
  460. # Option::ROM::PnP
  461. #
  462. ##############################################################################
  463. package Option::ROM::PnP;
  464. use strict;
  465. use warnings;
  466. use Carp;
  467. use bytes;
  468. sub new {
  469. my $class = shift;
  470. my $rom = shift;
  471. my $offset = shift;
  472. my $hash = {};
  473. tie %$hash, "Option::ROM::Fields", {
  474. rom => $rom,
  475. data => $rom->{data},
  476. offset => $offset,
  477. length => 0x06,
  478. fields => {
  479. signature => { offset => 0x00, length => 0x04, pack => "a4" },
  480. struct_revision =>{ offset => 0x04, length => 0x01, pack => "C" },
  481. struct_length => { offset => 0x05, length => 0x01, pack => "C" },
  482. checksum => { offset => 0x09, length => 0x01, pack => "C" },
  483. manufacturer => { offset => 0x0e, length => 0x02, pack => "S" },
  484. product => { offset => 0x10, length => 0x02, pack => "S" },
  485. bcv => { offset => 0x16, length => 0x02, pack => "S" },
  486. bdv => { offset => 0x18, length => 0x02, pack => "S" },
  487. bev => { offset => 0x1a, length => 0x02, pack => "S" },
  488. },
  489. };
  490. bless $hash, $class;
  491. my $self = tied ( %$hash );
  492. my $length = $rom->{rom}->length ();
  493. return undef unless ( $offset + $self->{length} <= $length &&
  494. $hash->{signature} eq Option::ROM::PNP_SIGNATURE &&
  495. $offset + $hash->{struct_length} * 16 <= $length );
  496. # Retrieve true length of structure
  497. $self->{length} = ( $hash->{struct_length} * 16 );
  498. return $hash;
  499. }
  500. sub checksum {
  501. my $hash = shift;
  502. my $self = tied(%$hash);
  503. return $self->checksum();
  504. }
  505. sub fix_checksum {
  506. my $hash = shift;
  507. my $self = tied(%$hash);
  508. $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
  509. }
  510. sub manufacturer {
  511. my $hash = shift;
  512. my $self = tied(%$hash);
  513. my $manufacturer = $hash->{manufacturer};
  514. return undef unless $manufacturer;
  515. my $raw = substr ( ${$self->{data}}, $manufacturer );
  516. return unpack ( "Z*", $raw );
  517. }
  518. sub product {
  519. my $hash = shift;
  520. my $self = tied(%$hash);
  521. my $product = $hash->{product};
  522. return undef unless $product;
  523. my $raw = substr ( ${$self->{data}}, $product );
  524. return unpack ( "Z*", $raw );
  525. }
  526. ##############################################################################
  527. #
  528. # Option::ROM::UNDI
  529. #
  530. ##############################################################################
  531. package Option::ROM::UNDI;
  532. use strict;
  533. use warnings;
  534. use Carp;
  535. use bytes;
  536. sub new {
  537. my $class = shift;
  538. my $rom = shift;
  539. my $offset = shift;
  540. my $hash = {};
  541. tie %$hash, "Option::ROM::Fields", {
  542. rom => $rom,
  543. data => $rom->{data},
  544. offset => $offset,
  545. length => 0x16,
  546. fields => {
  547. signature => { offset => 0x00, length => 0x04, pack => "a4" },
  548. struct_length => { offset => 0x04, length => 0x01, pack => "C" },
  549. checksum => { offset => 0x05, length => 0x01, pack => "C" },
  550. struct_revision =>{ offset => 0x06, length => 0x01, pack => "C" },
  551. version_revision =>{ offset => 0x07, length => 0x01, pack => "C" },
  552. version_minor => { offset => 0x08, length => 0x01, pack => "C" },
  553. version_major => { offset => 0x09, length => 0x01, pack => "C" },
  554. loader_entry => { offset => 0x0a, length => 0x02, pack => "S" },
  555. stack_size => { offset => 0x0c, length => 0x02, pack => "S" },
  556. data_size => { offset => 0x0e, length => 0x02, pack => "S" },
  557. code_size => { offset => 0x10, length => 0x02, pack => "S" },
  558. bus_type => { offset => 0x12, length => 0x04, pack => "a4" },
  559. },
  560. };
  561. bless $hash, $class;
  562. my $self = tied ( %$hash );
  563. my $length = $rom->{rom}->length ();
  564. return undef unless ( $offset + $self->{length} <= $length &&
  565. $hash->{signature} eq Option::ROM::UNDI_SIGNATURE &&
  566. $offset + $hash->{struct_length} <= $length );
  567. # Retrieve true length of structure
  568. $self->{length} = $hash->{struct_length};
  569. return $hash;
  570. }
  571. sub checksum {
  572. my $hash = shift;
  573. my $self = tied(%$hash);
  574. return $self->checksum();
  575. }
  576. sub fix_checksum {
  577. my $hash = shift;
  578. my $self = tied(%$hash);
  579. $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
  580. }
  581. ##############################################################################
  582. #
  583. # Option::ROM::iPXE
  584. #
  585. ##############################################################################
  586. package Option::ROM::iPXE;
  587. use strict;
  588. use warnings;
  589. use Carp;
  590. use bytes;
  591. sub new {
  592. my $class = shift;
  593. my $rom = shift;
  594. my $offset = shift;
  595. my $hash = {};
  596. tie %$hash, "Option::ROM::Fields", {
  597. rom => $rom,
  598. data => $rom->{data},
  599. offset => $offset,
  600. length => 0x06,
  601. fields => {
  602. signature => { offset => 0x00, length => 0x04, pack => "a4" },
  603. struct_length => { offset => 0x04, length => 0x01, pack => "C" },
  604. checksum => { offset => 0x05, length => 0x01, pack => "C" },
  605. shrunk_length => { offset => 0x06, length => 0x01, pack => "C" },
  606. build_id => { offset => 0x08, length => 0x04, pack => "L" },
  607. },
  608. };
  609. bless $hash, $class;
  610. my $self = tied ( %$hash );
  611. my $length = $rom->{rom}->length ();
  612. return undef unless ( $offset + $self->{length} <= $length &&
  613. $hash->{signature} eq Option::ROM::IPXE_SIGNATURE &&
  614. $offset + $hash->{struct_length} <= $length );
  615. # Retrieve true length of structure
  616. $self->{length} = $hash->{struct_length};
  617. return $hash;
  618. }
  619. sub checksum {
  620. my $hash = shift;
  621. my $self = tied(%$hash);
  622. return $self->checksum();
  623. }
  624. sub fix_checksum {
  625. my $hash = shift;
  626. my $self = tied(%$hash);
  627. $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
  628. }
  629. ##############################################################################
  630. #
  631. # Option::ROM::EFI
  632. #
  633. ##############################################################################
  634. package Option::ROM::EFI;
  635. use strict;
  636. use warnings;
  637. use Carp;
  638. use bytes;
  639. sub new {
  640. my $class = shift;
  641. my $rom = shift;
  642. my $pci = shift;
  643. my $hash = {};
  644. tie %$hash, "Option::ROM::Fields", {
  645. rom => $rom,
  646. data => $rom->{data},
  647. offset => 0x00,
  648. length => 0x18,
  649. fields => {
  650. signature => { offset => 0x00, length => 0x02, pack => "S" },
  651. init_size => { offset => 0x02, length => 0x02, pack => "S" },
  652. efi_signature => { offset => 0x04, length => 0x04, pack => "L" },
  653. efi_subsystem => { offset => 0x08, length => 0x02, pack => "S" },
  654. efi_machine_type => { offset => 0x0a, length => 0x02, pack => "S" },
  655. compression_type => { offset => 0x0c, length => 0x02, pack => "S" },
  656. efi_image_offset => { offset => 0x16, length => 0x02, pack => "S" },
  657. },
  658. };
  659. bless $hash, $class;
  660. my $self = tied ( %$hash );
  661. return undef unless ( $hash->{efi_signature} == Option::ROM::EFI_SIGNATURE &&
  662. $pci->{code_type} == 0x03 );
  663. return $hash;
  664. }
  665. 1;