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