1package Option::ROM;
2
3# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
18# 02110-1301, USA.
19
20=head1 NAME
21
22Option::ROM - Option ROM manipulation
23
24=head1 SYNOPSIS
25
26    use Option::ROM;
27
28    # Load a ROM image
29    my $rom = new Option::ROM;
30    $rom->load ( "rtl8139.rom" );
31
32    # Modify the PCI device ID
33    $rom->pci_header->{device_id} = 0x1234;
34    $rom->fix_checksum();
35
36    # Write ROM image out to a new file
37    $rom->save ( "rtl8139-modified.rom" );
38
39=head1 DESCRIPTION
40
41C<Option::ROM> provides a mechanism for manipulating Option ROM
42images.
43
44=head1 METHODS
45
46=cut
47
48##############################################################################
49#
50# Option::ROM::Fields
51#
52##############################################################################
53
54package Option::ROM::Fields;
55
56use strict;
57use warnings;
58use Carp;
59use bytes;
60
61sub TIEHASH {
62  my $class = shift;
63  my $self = shift;
64
65  bless $self, $class;
66  return $self;
67}
68
69sub FETCH {
70  my $self = shift;
71  my $key = shift;
72
73  return undef unless $self->EXISTS ( $key );
74  my $raw = substr ( ${$self->{data}},
75		     ( $self->{offset} + $self->{fields}->{$key}->{offset} ),
76		     $self->{fields}->{$key}->{length} );
77  my $unpack = ( ref $self->{fields}->{$key}->{unpack} ?
78		 $self->{fields}->{$key}->{unpack} :
79		 sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } );
80  return &$unpack ( $raw );
81}
82
83sub STORE {
84  my $self = shift;
85  my $key = shift;
86  my $value = shift;
87
88  croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key );
89  my $pack = ( ref $self->{fields}->{$key}->{pack} ?
90	       $self->{fields}->{$key}->{pack} :
91	       sub { pack ( $self->{fields}->{$key}->{pack}, shift ); } );
92  my $raw = &$pack ( $value );
93  substr ( ${$self->{data}},
94	   ( $self->{offset} + $self->{fields}->{$key}->{offset} ),
95	   $self->{fields}->{$key}->{length} ) = $raw;
96}
97
98sub DELETE {
99  my $self = shift;
100  my $key = shift;
101
102  $self->STORE ( $key, 0 );
103}
104
105sub CLEAR {
106  my $self = shift;
107
108  foreach my $key ( keys %{$self->{fields}} ) {
109    $self->DELETE ( $key );
110  }
111}
112
113sub EXISTS {
114  my $self = shift;
115  my $key = shift;
116
117  return ( exists $self->{fields}->{$key} &&
118	   ( ( $self->{fields}->{$key}->{offset} +
119	       $self->{fields}->{$key}->{length} ) <= $self->{length} ) &&
120	   ( ! defined $self->{fields}->{$key}->{check} ||
121	     &{$self->{fields}->{$key}->{check}} ( $self, $key ) ) );
122}
123
124sub FIRSTKEY {
125  my $self = shift;
126
127  keys %{$self->{fields}};
128  return each %{$self->{fields}};
129}
130
131sub NEXTKEY {
132  my $self = shift;
133  my $lastkey = shift;
134
135  return each %{$self->{fields}};
136}
137
138sub SCALAR {
139  my $self = shift;
140
141  return 1;
142}
143
144sub UNTIE {
145  my $self = shift;
146}
147
148sub DESTROY {
149  my $self = shift;
150}
151
152sub checksum {
153  my $self = shift;
154
155  my $raw = substr ( ${$self->{data}}, $self->{offset}, $self->{length} );
156  return unpack ( "%8C*", $raw );
157}
158
159##############################################################################
160#
161# Option::ROM
162#
163##############################################################################
164
165package Option::ROM;
166
167use strict;
168use warnings;
169use Carp;
170use bytes;
171use Exporter 'import';
172
173use constant ROM_SIGNATURE => 0xaa55;
174use constant PCI_SIGNATURE => 'PCIR';
175use constant PCI_LAST_IMAGE => 0x80;
176use constant PNP_SIGNATURE => '$PnP';
177use constant UNDI_SIGNATURE => 'UNDI';
178use constant IPXE_SIGNATURE => 'iPXE';
179use constant EFI_SIGNATURE => 0x00000ef1;
180
181our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
182		      PNP_SIGNATURE UNDI_SIGNATURE IPXE_SIGNATURE EFI_SIGNATURE );
183our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
184
185use constant JMP_SHORT => 0xeb;
186use constant JMP_NEAR => 0xe9;
187use constant CALL_NEAR => 0xe8;
188
189sub pack_init {
190  my $dest = shift;
191
192  # Always create a near jump; it's simpler
193  if ( $dest ) {
194    return pack ( "CS", JMP_NEAR, ( $dest - 6 ) );
195  } else {
196    return pack ( "CS", 0, 0 );
197  }
198}
199
200sub unpack_init {
201  my $instr = shift;
202
203  # Accept both short and near jumps
204  my $jump = unpack ( "C", $instr );
205  if ( $jump == JMP_SHORT ) {
206    my $offset = unpack ( "xC", $instr );
207    return ( $offset + 5 );
208  } elsif ( $jump == JMP_NEAR ) {
209    my $offset = unpack ( "xS", $instr );
210    return ( $offset + 6 );
211  } elsif ( $jump == CALL_NEAR ) {
212    my $offset = unpack ( "xS", $instr );
213    return ( $offset + 6 );
214  } elsif ( $jump == 0 ) {
215    return 0;
216  } else {
217    carp "Unrecognised jump instruction in init vector\n";
218    return 0;
219  }
220}
221
222sub check_pcat_rom {
223  my $self = shift;
224  my $key = shift;
225
226  my $pci = $self->{rom}->pci_header ();
227
228  return ! defined $pci || $pci->{code_type} == 0x00;
229}
230
231=pod
232
233=item C<< new () >>
234
235Construct a new C<Option::ROM> object.
236
237=cut
238
239sub new {
240  my $class = shift;
241
242  my $hash = {};
243  tie %$hash, "Option::ROM::Fields", {
244    rom => $hash, # ROM object itself
245    data => undef,
246    offset => 0x00,
247    length => 0x20,
248    file_offset => 0x0,
249    fields => {
250      signature =>	{ offset => 0x00, length => 0x02, pack => "S" },
251      length =>		{ offset => 0x02, length => 0x01, pack => "C" },
252      # "init" is part of a jump instruction
253      init =>		{ offset => 0x03, length => 0x03,
254			  pack => \&pack_init, unpack => \&unpack_init,
255			  check => \&check_pcat_rom },
256      checksum =>	{ offset => 0x06, length => 0x01, pack => "C",
257			  check => \&check_pcat_rom },
258      ipxe_header =>	{ offset => 0x10, length => 0x02, pack => "S",
259			  check => \&check_pcat_rom },
260      bofm_header =>	{ offset => 0x14, length => 0x02, pack => "S",
261			  check => \&check_pcat_rom },
262      undi_header =>	{ offset => 0x16, length => 0x02, pack => "S",
263			  check => \&check_pcat_rom },
264      pci_header =>	{ offset => 0x18, length => 0x02, pack => "S" },
265      pnp_header =>	{ offset => 0x1a, length => 0x02, pack => "S",
266			  check => \&check_pcat_rom },
267    },
268  };
269  bless $hash, $class;
270  return $hash;
271}
272
273=pod
274
275=item C<< set ( $data [, $file_offset ] ) >>
276
277Set option ROM contents, optionally sets original file offset.
278
279=cut
280
281sub set {
282  my $hash = shift;
283  my $self = tied(%$hash);
284  my $data = shift;
285  my $file_offset = shift // 0x0;
286
287  # Store data
288  $self->{data} = \$data;
289  $self->{file_offset} = $file_offset;
290
291  # Split out any data belonging to the next image
292  delete $self->{next_image};
293  my $pci_header = $hash->pci_header();
294  if ( ( defined $pci_header ) &&
295       ( ! ( $pci_header->{last_image} & PCI_LAST_IMAGE ) ) ) {
296    my $length = ( $pci_header->{image_length} * 512 );
297    my $remainder = substr ( $data, $length );
298    $data = substr ( $data, 0, $length );
299    $self->{next_image} = new Option::ROM;
300    $self->{next_image}->set ( $remainder, $self->{file_offset} + $length );
301  }
302}
303
304=pod
305
306=item C<< get () >>
307
308Get option ROM contents.
309
310=cut
311
312sub get {
313  my $hash = shift;
314  my $self = tied(%$hash);
315
316  my $data = ${$self->{data}};
317  $data .= $self->{next_image}->get() if $self->{next_image};
318  return $data;
319}
320
321=pod
322
323=item C<< load ( $filename ) >>
324
325Load option ROM contents from the file C<$filename>.
326
327=cut
328
329sub load {
330  my $hash = shift;
331  my $self = tied(%$hash);
332  my $filename = shift;
333
334  $self->{filename} = $filename;
335
336  open my $fh, "<$filename"
337      or croak "Cannot open $filename for reading: $!";
338  binmode $fh;
339  read $fh, my $data, -s $fh;
340  $hash->set ( $data );
341  close $fh;
342}
343
344=pod
345
346=item C<< save ( [ $filename ] ) >>
347
348Write the ROM data back out to the file C<$filename>.  If C<$filename>
349is omitted, the file used in the call to C<load()> will be used.
350
351=cut
352
353sub save {
354  my $hash = shift;
355  my $self = tied(%$hash);
356  my $filename = shift;
357
358  $filename ||= $self->{filename};
359
360  open my $fh, ">$filename"
361      or croak "Cannot open $filename for writing: $!";
362  my $data = $hash->get();
363  binmode $fh;
364  print $fh $data;
365  close $fh;
366}
367
368=pod
369
370=item C<< length () >>
371
372Length of option ROM data.  This is the length of the file, not the
373length from the ROM header length field.
374
375=cut
376
377sub length {
378  my $hash = shift;
379  my $self = tied(%$hash);
380
381  return length ${$self->{data}};
382}
383
384=pod
385
386=item C<< pci_header () >>
387
388Return a C<Option::ROM::PCI> object representing the ROM's PCI header,
389if present.
390
391=cut
392
393sub pci_header {
394  my $hash = shift;
395  my $self = tied(%$hash);
396
397  my $offset = $hash->{pci_header};
398  return undef unless $offset;
399
400  return Option::ROM::PCI->new ( $self, $offset );
401}
402
403=pod
404
405=item C<< pnp_header () >>
406
407Return a C<Option::ROM::PnP> object representing the ROM's PnP header,
408if present.
409
410=cut
411
412sub pnp_header {
413  my $hash = shift;
414  my $self = tied(%$hash);
415
416  my $offset = $hash->{pnp_header};
417  return undef unless $offset;
418
419  return Option::ROM::PnP->new ( $self, $offset );
420}
421
422=pod
423
424=item C<< undi_header () >>
425
426Return a C<Option::ROM::UNDI> object representing the ROM's UNDI header,
427if present.
428
429=cut
430
431sub undi_header {
432  my $hash = shift;
433  my $self = tied(%$hash);
434
435  my $offset = $hash->{undi_header};
436  return undef unless $offset;
437
438  return Option::ROM::UNDI->new ( $self, $offset );
439}
440
441=pod
442
443=item C<< ipxe_header () >>
444
445Return a C<Option::ROM::iPXE> object representing the ROM's iPXE
446header, if present.
447
448=cut
449
450sub ipxe_header {
451  my $hash = shift;
452  my $self = tied(%$hash);
453
454  my $offset = $hash->{ipxe_header};
455  return undef unless $offset;
456
457  return Option::ROM::iPXE->new ( $self, $offset );
458}
459
460=pod
461
462=item C<< efi_header () >>
463
464Return a C<Option::ROM::EFI> object representing the ROM's EFI header,
465if present.
466
467=cut
468
469sub efi_header {
470  my $hash = shift;
471  my $self = tied(%$hash);
472
473  my $pci = $hash->pci_header ();
474  return undef unless defined $pci;
475
476  return Option::ROM::EFI->new ( $self, $pci );
477}
478
479=pod
480
481=item C<< next_image () >>
482
483Return a C<Option::ROM> object representing the next image within the
484ROM, if present.
485
486=cut
487
488sub next_image {
489  my $hash = shift;
490  my $self = tied(%$hash);
491
492  return $self->{next_image};
493}
494
495=pod
496
497=item C<< checksum () >>
498
499Calculate the byte checksum of the ROM.
500
501=cut
502
503sub checksum {
504  my $hash = shift;
505  my $self = tied(%$hash);
506
507  my $raw = substr ( ${$self->{data}}, 0, ( $hash->{length} * 512 ) );
508  return unpack ( "%8C*", $raw );
509}
510
511=pod
512
513=item C<< fix_checksum () >>
514
515Fix the byte checksum of the ROM.
516
517=cut
518
519sub fix_checksum {
520  my $hash = shift;
521  my $self = tied(%$hash);
522
523  return unless ( exists $hash->{checksum} );
524  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
525}
526
527=pod
528
529=item C<< file_offset () >>
530
531Get file offset of image.
532
533=cut
534
535sub file_offset {
536  my $hash = shift;
537  my $self = tied(%$hash);
538
539  return $self->{file_offset};
540}
541
542##############################################################################
543#
544# Option::ROM::PCI
545#
546##############################################################################
547
548package Option::ROM::PCI;
549
550use strict;
551use warnings;
552use Carp;
553use bytes;
554
555sub new {
556  my $class = shift;
557  my $rom = shift;
558  my $offset = shift;
559
560  my $hash = {};
561  tie %$hash, "Option::ROM::Fields", {
562    rom => $rom,
563    data => $rom->{data},
564    offset => $offset,
565    length => 0x0c,
566    fields => {
567      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" },
568      vendor_id =>	{ offset => 0x04, length => 0x02, pack => "S" },
569      device_id =>	{ offset => 0x06, length => 0x02, pack => "S" },
570      device_list =>	{ offset => 0x08, length => 0x02, pack => "S" },
571      struct_length =>	{ offset => 0x0a, length => 0x02, pack => "S" },
572      struct_revision =>{ offset => 0x0c, length => 0x01, pack => "C" },
573      prog_intf => 	{ offset => 0x0d, length => 0x01, pack => "C" },
574      sub_class => 	{ offset => 0x0e, length => 0x01, pack => "C" },
575      base_class => 	{ offset => 0x0f, length => 0x01, pack => "C" },
576      image_length =>	{ offset => 0x10, length => 0x02, pack => "S" },
577      revision =>	{ offset => 0x12, length => 0x02, pack => "S" },
578      code_type => 	{ offset => 0x14, length => 0x01, pack => "C" },
579      last_image => 	{ offset => 0x15, length => 0x01, pack => "C" },
580      runtime_length =>	{ offset => 0x16, length => 0x02, pack => "S" },
581      conf_header =>	{ offset => 0x18, length => 0x02, pack => "S" },
582      clp_entry =>	{ offset => 0x1a, length => 0x02, pack => "S" },
583    },
584  };
585  bless $hash, $class;
586
587  my $self = tied ( %$hash );
588  my $length = $rom->{rom}->length ();
589
590  return undef unless ( $offset + $self->{length} <= $length &&
591			$hash->{signature} eq Option::ROM::PCI_SIGNATURE &&
592			$offset + $hash->{struct_length} <= $length );
593
594  # Retrieve true length of structure
595  $self->{length} = $hash->{struct_length};
596
597  return $hash;
598}
599
600sub device_list {
601  my $hash = shift;
602  my $self = tied(%$hash);
603
604  my $device_list = $hash->{device_list};
605  return undef unless $device_list;
606
607  my @ids;
608  my $offset = ( $self->{offset} + $device_list );
609  while ( 1 ) {
610    my $raw = substr ( ${$self->{data}}, $offset, 2 );
611    my $id = unpack ( "S", $raw );
612    last unless $id;
613    push @ids, $id;
614    $offset += 2;
615  }
616
617  return @ids;
618}
619
620##############################################################################
621#
622# Option::ROM::PnP
623#
624##############################################################################
625
626package Option::ROM::PnP;
627
628use strict;
629use warnings;
630use Carp;
631use bytes;
632
633sub new {
634  my $class = shift;
635  my $rom = shift;
636  my $offset = shift;
637
638  my $hash = {};
639  tie %$hash, "Option::ROM::Fields", {
640    rom => $rom,
641    data => $rom->{data},
642    offset => $offset,
643    length => 0x06,
644    fields => {
645      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" },
646      struct_revision =>{ offset => 0x04, length => 0x01, pack => "C" },
647      struct_length =>	{ offset => 0x05, length => 0x01, pack => "C" },
648      checksum =>	{ offset => 0x09, length => 0x01, pack => "C" },
649      manufacturer =>	{ offset => 0x0e, length => 0x02, pack => "S" },
650      product =>	{ offset => 0x10, length => 0x02, pack => "S" },
651      bcv =>		{ offset => 0x16, length => 0x02, pack => "S" },
652      bdv =>		{ offset => 0x18, length => 0x02, pack => "S" },
653      bev =>		{ offset => 0x1a, length => 0x02, pack => "S" },
654    },
655  };
656  bless $hash, $class;
657
658  my $self = tied ( %$hash );
659  my $length = $rom->{rom}->length ();
660
661  return undef unless ( $offset + $self->{length} <= $length &&
662			$hash->{signature} eq Option::ROM::PNP_SIGNATURE &&
663			$offset + $hash->{struct_length} * 16 <= $length );
664
665  # Retrieve true length of structure
666  $self->{length} = ( $hash->{struct_length} * 16 );
667
668  return $hash;
669}
670
671sub checksum {
672  my $hash = shift;
673  my $self = tied(%$hash);
674
675  return $self->checksum();
676}
677
678sub fix_checksum {
679  my $hash = shift;
680  my $self = tied(%$hash);
681
682  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
683}
684
685sub manufacturer {
686  my $hash = shift;
687  my $self = tied(%$hash);
688
689  my $manufacturer = $hash->{manufacturer};
690  return undef unless $manufacturer;
691
692  my $raw = substr ( ${$self->{data}}, $manufacturer );
693  return unpack ( "Z*", $raw );
694}
695
696sub product {
697  my $hash = shift;
698  my $self = tied(%$hash);
699
700  my $product = $hash->{product};
701  return undef unless $product;
702
703  my $raw = substr ( ${$self->{data}}, $product );
704  return unpack ( "Z*", $raw );
705}
706
707##############################################################################
708#
709# Option::ROM::UNDI
710#
711##############################################################################
712
713package Option::ROM::UNDI;
714
715use strict;
716use warnings;
717use Carp;
718use bytes;
719
720sub new {
721  my $class = shift;
722  my $rom = shift;
723  my $offset = shift;
724
725  my $hash = {};
726  tie %$hash, "Option::ROM::Fields", {
727    rom => $rom,
728    data => $rom->{data},
729    offset => $offset,
730    length => 0x16,
731    fields => {
732      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" },
733      struct_length =>	{ offset => 0x04, length => 0x01, pack => "C" },
734      checksum =>	{ offset => 0x05, length => 0x01, pack => "C" },
735      struct_revision =>{ offset => 0x06, length => 0x01, pack => "C" },
736      version_revision =>{ offset => 0x07, length => 0x01, pack => "C" },
737      version_minor =>	{ offset => 0x08, length => 0x01, pack => "C" },
738      version_major =>	{ offset => 0x09, length => 0x01, pack => "C" },
739      loader_entry =>	{ offset => 0x0a, length => 0x02, pack => "S" },
740      stack_size =>	{ offset => 0x0c, length => 0x02, pack => "S" },
741      data_size =>	{ offset => 0x0e, length => 0x02, pack => "S" },
742      code_size =>	{ offset => 0x10, length => 0x02, pack => "S" },
743      bus_type =>	{ offset => 0x12, length => 0x04, pack => "a4" },
744    },
745  };
746  bless $hash, $class;
747
748  my $self = tied ( %$hash );
749  my $length = $rom->{rom}->length ();
750
751  return undef unless ( $offset + $self->{length} <= $length &&
752			$hash->{signature} eq Option::ROM::UNDI_SIGNATURE &&
753			$offset + $hash->{struct_length} <= $length );
754
755  # Retrieve true length of structure
756  $self->{length} = $hash->{struct_length};
757
758  return $hash;
759}
760
761sub checksum {
762  my $hash = shift;
763  my $self = tied(%$hash);
764
765  return $self->checksum();
766}
767
768sub fix_checksum {
769  my $hash = shift;
770  my $self = tied(%$hash);
771
772  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
773}
774
775##############################################################################
776#
777# Option::ROM::iPXE
778#
779##############################################################################
780
781package Option::ROM::iPXE;
782
783use strict;
784use warnings;
785use Carp;
786use bytes;
787
788sub new {
789  my $class = shift;
790  my $rom = shift;
791  my $offset = shift;
792
793  my $hash = {};
794  tie %$hash, "Option::ROM::Fields", {
795    rom => $rom,
796    data => $rom->{data},
797    offset => $offset,
798    length => 0x06,
799    fields => {
800      signature =>	{ offset => 0x00, length => 0x04, pack => "a4" },
801      struct_length =>	{ offset => 0x04, length => 0x01, pack => "C" },
802      checksum =>	{ offset => 0x05, length => 0x01, pack => "C" },
803      shrunk_length =>	{ offset => 0x06, length => 0x01, pack => "C" },
804      build_id =>	{ offset => 0x08, length => 0x04, pack => "L" },
805    },
806  };
807  bless $hash, $class;
808
809  my $self = tied ( %$hash );
810  my $length = $rom->{rom}->length ();
811
812  return undef unless ( $offset + $self->{length} <= $length &&
813			$hash->{signature} eq Option::ROM::IPXE_SIGNATURE &&
814			$offset + $hash->{struct_length} <= $length );
815
816  # Retrieve true length of structure
817  $self->{length} = $hash->{struct_length};
818
819  return $hash;
820}
821
822sub checksum {
823  my $hash = shift;
824  my $self = tied(%$hash);
825
826  return $self->checksum();
827}
828
829sub fix_checksum {
830  my $hash = shift;
831  my $self = tied(%$hash);
832
833  $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
834}
835
836##############################################################################
837#
838# Option::ROM::EFI
839#
840##############################################################################
841
842package Option::ROM::EFI;
843
844use strict;
845use warnings;
846use Carp;
847use bytes;
848
849sub new {
850  my $class = shift;
851  my $rom = shift;
852  my $pci = shift;
853
854  my $hash = {};
855  tie %$hash, "Option::ROM::Fields", {
856    rom => $rom,
857    data => $rom->{data},
858    offset => 0x00,
859    length => 0x18,
860    fields => {
861      signature =>		{ offset => 0x00, length => 0x02, pack => "S" },
862      init_size =>		{ offset => 0x02, length => 0x02, pack => "S" },
863      efi_signature =>		{ offset => 0x04, length => 0x04, pack => "L" },
864      efi_subsystem =>		{ offset => 0x08, length => 0x02, pack => "S" },
865      efi_machine_type =>	{ offset => 0x0a, length => 0x02, pack => "S" },
866      compression_type =>	{ offset => 0x0c, length => 0x02, pack => "S" },
867      efi_image_offset =>	{ offset => 0x16, length => 0x02, pack => "S" },
868    },
869  };
870  bless $hash, $class;
871
872  my $self = tied ( %$hash );
873
874  return undef unless ( $hash->{efi_signature} == Option::ROM::EFI_SIGNATURE &&
875			$pci->{code_type} == 0x03 );
876
877  return $hash;
878}
879
8801;
881