|
@@ -116,7 +116,9 @@ sub EXISTS {
|
116
|
116
|
|
117
|
117
|
return ( exists $self->{fields}->{$key} &&
|
118
|
118
|
( ( $self->{fields}->{$key}->{offset} +
|
119
|
|
- $self->{fields}->{$key}->{length} ) <= $self->{length} ) );
|
|
119
|
+ $self->{fields}->{$key}->{length} ) <= $self->{length} ) &&
|
|
120
|
+ ( ! defined $self->{fields}->{$key}->{check} ||
|
|
121
|
+ &{$self->{fields}->{$key}->{check}} ( $self, $key ) ) );
|
120
|
122
|
}
|
121
|
123
|
|
122
|
124
|
sub FIRSTKEY {
|
|
@@ -172,10 +174,11 @@ use constant ROM_SIGNATURE => 0xaa55;
|
172
|
174
|
use constant PCI_SIGNATURE => 'PCIR';
|
173
|
175
|
use constant PCI_LAST_IMAGE => 0x80;
|
174
|
176
|
use constant PNP_SIGNATURE => '$PnP';
|
|
177
|
+use constant UNDI_SIGNATURE => 'UNDI';
|
175
|
178
|
use constant IPXE_SIGNATURE => 'iPXE';
|
176
|
179
|
|
177
|
180
|
our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
|
178
|
|
- PNP_SIGNATURE IPXE_SIGNATURE );
|
|
181
|
+ PNP_SIGNATURE UNDI_SIGNATURE IPXE_SIGNATURE );
|
179
|
182
|
our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
|
180
|
183
|
|
181
|
184
|
use constant JMP_SHORT => 0xeb;
|
|
@@ -210,10 +213,20 @@ sub unpack_init {
|
210
|
213
|
} elsif ( $jump == 0 ) {
|
211
|
214
|
return 0;
|
212
|
215
|
} else {
|
213
|
|
- croak "Unrecognised jump instruction in init vector\n";
|
|
216
|
+ carp "Unrecognised jump instruction in init vector\n";
|
|
217
|
+ return 0;
|
214
|
218
|
}
|
215
|
219
|
}
|
216
|
220
|
|
|
221
|
+sub check_pcat_rom {
|
|
222
|
+ my $self = shift;
|
|
223
|
+ my $key = shift;
|
|
224
|
+
|
|
225
|
+ my $pci = $self->{rom}->pci_header ();
|
|
226
|
+
|
|
227
|
+ return ! defined $pci || $pci->{code_type} == 0x00;
|
|
228
|
+}
|
|
229
|
+
|
217
|
230
|
=pod
|
218
|
231
|
|
219
|
232
|
=item C<< new () >>
|
|
@@ -227,21 +240,29 @@ sub new {
|
227
|
240
|
|
228
|
241
|
my $hash = {};
|
229
|
242
|
tie %$hash, "Option::ROM::Fields", {
|
|
243
|
+ rom => $hash, # ROM object itself
|
230
|
244
|
data => undef,
|
231
|
245
|
offset => 0x00,
|
232
|
246
|
length => 0x20,
|
|
247
|
+ file_offset => 0x0,
|
233
|
248
|
fields => {
|
234
|
249
|
signature => { offset => 0x00, length => 0x02, pack => "S" },
|
235
|
250
|
length => { offset => 0x02, length => 0x01, pack => "C" },
|
236
|
251
|
# "init" is part of a jump instruction
|
237
|
252
|
init => { offset => 0x03, length => 0x03,
|
238
|
|
- pack => \&pack_init, unpack => \&unpack_init },
|
239
|
|
- checksum => { offset => 0x06, length => 0x01, pack => "C" },
|
240
|
|
- ipxe_header => { offset => 0x10, length => 0x02, pack => "S" },
|
241
|
|
- bofm_header => { offset => 0x14, length => 0x02, pack => "S" },
|
242
|
|
- undi_header => { offset => 0x16, length => 0x02, pack => "S" },
|
|
253
|
+ pack => \&pack_init, unpack => \&unpack_init,
|
|
254
|
+ check => \&check_pcat_rom },
|
|
255
|
+ checksum => { offset => 0x06, length => 0x01, pack => "C",
|
|
256
|
+ check => \&check_pcat_rom },
|
|
257
|
+ ipxe_header => { offset => 0x10, length => 0x02, pack => "S",
|
|
258
|
+ check => \&check_pcat_rom },
|
|
259
|
+ bofm_header => { offset => 0x14, length => 0x02, pack => "S",
|
|
260
|
+ check => \&check_pcat_rom },
|
|
261
|
+ undi_header => { offset => 0x16, length => 0x02, pack => "S",
|
|
262
|
+ check => \&check_pcat_rom },
|
243
|
263
|
pci_header => { offset => 0x18, length => 0x02, pack => "S" },
|
244
|
|
- pnp_header => { offset => 0x1a, length => 0x02, pack => "S" },
|
|
264
|
+ pnp_header => { offset => 0x1a, length => 0x02, pack => "S",
|
|
265
|
+ check => \&check_pcat_rom },
|
245
|
266
|
},
|
246
|
267
|
};
|
247
|
268
|
bless $hash, $class;
|
|
@@ -250,9 +271,9 @@ sub new {
|
250
|
271
|
|
251
|
272
|
=pod
|
252
|
273
|
|
253
|
|
-=item C<< set ( $data ) >>
|
|
274
|
+=item C<< set ( $data [, $file_offset ] ) >>
|
254
|
275
|
|
255
|
|
-Set option ROM contents.
|
|
276
|
+Set option ROM contents, optionally sets original file offset.
|
256
|
277
|
|
257
|
278
|
=cut
|
258
|
279
|
|
|
@@ -260,9 +281,11 @@ sub set {
|
260
|
281
|
my $hash = shift;
|
261
|
282
|
my $self = tied(%$hash);
|
262
|
283
|
my $data = shift;
|
|
284
|
+ my $file_offset = shift // 0x0;
|
263
|
285
|
|
264
|
286
|
# Store data
|
265
|
287
|
$self->{data} = \$data;
|
|
288
|
+ $self->{file_offset} = $file_offset;
|
266
|
289
|
|
267
|
290
|
# Split out any data belonging to the next image
|
268
|
291
|
delete $self->{next_image};
|
|
@@ -273,7 +296,7 @@ sub set {
|
273
|
296
|
my $remainder = substr ( $data, $length );
|
274
|
297
|
$data = substr ( $data, 0, $length );
|
275
|
298
|
$self->{next_image} = new Option::ROM;
|
276
|
|
- $self->{next_image}->set ( $remainder );
|
|
299
|
+ $self->{next_image}->set ( $remainder, $self->{file_offset} + $length );
|
277
|
300
|
}
|
278
|
301
|
}
|
279
|
302
|
|
|
@@ -311,6 +334,7 @@ sub load {
|
311
|
334
|
|
312
|
335
|
open my $fh, "<$filename"
|
313
|
336
|
or croak "Cannot open $filename for reading: $!";
|
|
337
|
+ binmode $fh;
|
314
|
338
|
read $fh, my $data, -s $fh;
|
315
|
339
|
$hash->set ( $data );
|
316
|
340
|
close $fh;
|
|
@@ -335,6 +359,7 @@ sub save {
|
335
|
359
|
open my $fh, ">$filename"
|
336
|
360
|
or croak "Cannot open $filename for writing: $!";
|
337
|
361
|
my $data = $hash->get();
|
|
362
|
+ binmode $fh;
|
338
|
363
|
print $fh $data;
|
339
|
364
|
close $fh;
|
340
|
365
|
}
|
|
@@ -369,9 +394,9 @@ sub pci_header {
|
369
|
394
|
my $self = tied(%$hash);
|
370
|
395
|
|
371
|
396
|
my $offset = $hash->{pci_header};
|
372
|
|
- return undef unless $offset != 0;
|
|
397
|
+ return undef unless $offset;
|
373
|
398
|
|
374
|
|
- return Option::ROM::PCI->new ( $self->{data}, $offset );
|
|
399
|
+ return Option::ROM::PCI->new ( $self, $offset );
|
375
|
400
|
}
|
376
|
401
|
|
377
|
402
|
=pod
|
|
@@ -388,9 +413,9 @@ sub pnp_header {
|
388
|
413
|
my $self = tied(%$hash);
|
389
|
414
|
|
390
|
415
|
my $offset = $hash->{pnp_header};
|
391
|
|
- return undef unless $offset != 0;
|
|
416
|
+ return undef unless $offset;
|
392
|
417
|
|
393
|
|
- return Option::ROM::PnP->new ( $self->{data}, $offset );
|
|
418
|
+ return Option::ROM::PnP->new ( $self, $offset );
|
394
|
419
|
}
|
395
|
420
|
|
396
|
421
|
=pod
|
|
@@ -407,9 +432,9 @@ sub undi_header {
|
407
|
432
|
my $self = tied(%$hash);
|
408
|
433
|
|
409
|
434
|
my $offset = $hash->{undi_header};
|
410
|
|
- return undef unless $offset != 0;
|
|
435
|
+ return undef unless $offset;
|
411
|
436
|
|
412
|
|
- return Option::ROM::UNDI->new ( $self->{data}, $offset );
|
|
437
|
+ return Option::ROM::UNDI->new ( $self, $offset );
|
413
|
438
|
}
|
414
|
439
|
|
415
|
440
|
=pod
|
|
@@ -426,9 +451,9 @@ sub ipxe_header {
|
426
|
451
|
my $self = tied(%$hash);
|
427
|
452
|
|
428
|
453
|
my $offset = $hash->{ipxe_header};
|
429
|
|
- return undef unless $offset != 0;
|
|
454
|
+ return undef unless $offset;
|
430
|
455
|
|
431
|
|
- return Option::ROM::iPXE->new ( $self->{data}, $offset );
|
|
456
|
+ return Option::ROM::iPXE->new ( $self, $offset );
|
432
|
457
|
}
|
433
|
458
|
|
434
|
459
|
=pod
|
|
@@ -475,9 +500,25 @@ sub fix_checksum {
|
475
|
500
|
my $hash = shift;
|
476
|
501
|
my $self = tied(%$hash);
|
477
|
502
|
|
|
503
|
+ return unless ( exists $hash->{checksum} );
|
478
|
504
|
$hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
|
479
|
505
|
}
|
480
|
506
|
|
|
507
|
+=pod
|
|
508
|
+
|
|
509
|
+=item C<< file_offset () >>
|
|
510
|
+
|
|
511
|
+Get file offset of image.
|
|
512
|
+
|
|
513
|
+=cut
|
|
514
|
+
|
|
515
|
+sub file_offset {
|
|
516
|
+ my $hash = shift;
|
|
517
|
+ my $self = tied(%$hash);
|
|
518
|
+
|
|
519
|
+ return $self->{file_offset};
|
|
520
|
+}
|
|
521
|
+
|
481
|
522
|
##############################################################################
|
482
|
523
|
#
|
483
|
524
|
# Option::ROM::PCI
|
|
@@ -493,12 +534,13 @@ use bytes;
|
493
|
534
|
|
494
|
535
|
sub new {
|
495
|
536
|
my $class = shift;
|
496
|
|
- my $data = shift;
|
|
537
|
+ my $rom = shift;
|
497
|
538
|
my $offset = shift;
|
498
|
539
|
|
499
|
540
|
my $hash = {};
|
500
|
541
|
tie %$hash, "Option::ROM::Fields", {
|
501
|
|
- data => $data,
|
|
542
|
+ rom => $rom,
|
|
543
|
+ data => $rom->{data},
|
502
|
544
|
offset => $offset,
|
503
|
545
|
length => 0x0c,
|
504
|
546
|
fields => {
|
|
@@ -522,11 +564,17 @@ sub new {
|
522
|
564
|
};
|
523
|
565
|
bless $hash, $class;
|
524
|
566
|
|
525
|
|
- # Retrieve true length of structure
|
526
|
567
|
my $self = tied ( %$hash );
|
|
568
|
+ my $length = $rom->{rom}->length ();
|
|
569
|
+
|
|
570
|
+ return undef unless ( $offset + $self->{length} <= $length &&
|
|
571
|
+ $hash->{signature} eq Option::ROM::PCI_SIGNATURE &&
|
|
572
|
+ $offset + $hash->{struct_length} <= $length );
|
|
573
|
+
|
|
574
|
+ # Retrieve true length of structure
|
527
|
575
|
$self->{length} = $hash->{struct_length};
|
528
|
576
|
|
529
|
|
- return $hash;
|
|
577
|
+ return $hash;
|
530
|
578
|
}
|
531
|
579
|
|
532
|
580
|
sub device_list {
|
|
@@ -564,12 +612,13 @@ use bytes;
|
564
|
612
|
|
565
|
613
|
sub new {
|
566
|
614
|
my $class = shift;
|
567
|
|
- my $data = shift;
|
|
615
|
+ my $rom = shift;
|
568
|
616
|
my $offset = shift;
|
569
|
617
|
|
570
|
618
|
my $hash = {};
|
571
|
619
|
tie %$hash, "Option::ROM::Fields", {
|
572
|
|
- data => $data,
|
|
620
|
+ rom => $rom,
|
|
621
|
+ data => $rom->{data},
|
573
|
622
|
offset => $offset,
|
574
|
623
|
length => 0x06,
|
575
|
624
|
fields => {
|
|
@@ -586,11 +635,17 @@ sub new {
|
586
|
635
|
};
|
587
|
636
|
bless $hash, $class;
|
588
|
637
|
|
589
|
|
- # Retrieve true length of structure
|
590
|
638
|
my $self = tied ( %$hash );
|
|
639
|
+ my $length = $rom->{rom}->length ();
|
|
640
|
+
|
|
641
|
+ return undef unless ( $offset + $self->{length} <= $length &&
|
|
642
|
+ $hash->{signature} eq Option::ROM::PNP_SIGNATURE &&
|
|
643
|
+ $offset + $hash->{struct_length} * 16 <= $length );
|
|
644
|
+
|
|
645
|
+ # Retrieve true length of structure
|
591
|
646
|
$self->{length} = ( $hash->{struct_length} * 16 );
|
592
|
647
|
|
593
|
|
- return $hash;
|
|
648
|
+ return $hash;
|
594
|
649
|
}
|
595
|
650
|
|
596
|
651
|
sub checksum {
|
|
@@ -644,12 +699,13 @@ use bytes;
|
644
|
699
|
|
645
|
700
|
sub new {
|
646
|
701
|
my $class = shift;
|
647
|
|
- my $data = shift;
|
|
702
|
+ my $rom = shift;
|
648
|
703
|
my $offset = shift;
|
649
|
704
|
|
650
|
705
|
my $hash = {};
|
651
|
706
|
tie %$hash, "Option::ROM::Fields", {
|
652
|
|
- data => $data,
|
|
707
|
+ rom => $rom,
|
|
708
|
+ data => $rom->{data},
|
653
|
709
|
offset => $offset,
|
654
|
710
|
length => 0x16,
|
655
|
711
|
fields => {
|
|
@@ -669,8 +725,14 @@ sub new {
|
669
|
725
|
};
|
670
|
726
|
bless $hash, $class;
|
671
|
727
|
|
672
|
|
- # Retrieve true length of structure
|
673
|
728
|
my $self = tied ( %$hash );
|
|
729
|
+ my $length = $rom->{rom}->length ();
|
|
730
|
+
|
|
731
|
+ return undef unless ( $offset + $self->{length} <= $length &&
|
|
732
|
+ $hash->{signature} eq Option::ROM::UNDI_SIGNATURE &&
|
|
733
|
+ $offset + $hash->{struct_length} <= $length );
|
|
734
|
+
|
|
735
|
+ # Retrieve true length of structure
|
674
|
736
|
$self->{length} = $hash->{struct_length};
|
675
|
737
|
|
676
|
738
|
return $hash;
|
|
@@ -705,12 +767,13 @@ use bytes;
|
705
|
767
|
|
706
|
768
|
sub new {
|
707
|
769
|
my $class = shift;
|
708
|
|
- my $data = shift;
|
|
770
|
+ my $rom = shift;
|
709
|
771
|
my $offset = shift;
|
710
|
772
|
|
711
|
773
|
my $hash = {};
|
712
|
774
|
tie %$hash, "Option::ROM::Fields", {
|
713
|
|
- data => $data,
|
|
775
|
+ rom => $rom,
|
|
776
|
+ data => $rom->{data},
|
714
|
777
|
offset => $offset,
|
715
|
778
|
length => 0x06,
|
716
|
779
|
fields => {
|
|
@@ -723,8 +786,14 @@ sub new {
|
723
|
786
|
};
|
724
|
787
|
bless $hash, $class;
|
725
|
788
|
|
726
|
|
- # Retrieve true length of structure
|
727
|
789
|
my $self = tied ( %$hash );
|
|
790
|
+ my $length = $rom->{rom}->length ();
|
|
791
|
+
|
|
792
|
+ return undef unless ( $offset + $self->{length} <= $length &&
|
|
793
|
+ $hash->{signature} eq Option::ROM::IPXE_SIGNATURE &&
|
|
794
|
+ $offset + $hash->{struct_length} <= $length );
|
|
795
|
+
|
|
796
|
+ # Retrieve true length of structure
|
728
|
797
|
$self->{length} = $hash->{struct_length};
|
729
|
798
|
|
730
|
799
|
return $hash;
|