forked from Mellanox/bfscripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mlx-mkbfb
executable file
·913 lines (748 loc) · 30.8 KB
/
mlx-mkbfb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
#!/usr/bin/env python3
#
# Copyright 2017-2019 Mellanox Technologies. All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of Mellanox nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
"""
Create or dump a BlueField boot stream.
"""
import binascii
import functools
import operator
import os
from io import StringIO
import struct
import sys
from optparse import OptionParser, OptionGroup
# Table of all possible image types. Order is significant, since we
# will emit images in this order unless overridden by the --expert
# switch. ATF image IDs match those used internally by ATF; see
# atf/include/common/tbbr/tbbr_img_def.h. UEFI image IDs are defined by
# us; see edk2/MlxPlatformPkg/Filesystem/BfbFs/BfbFs.c.
#
# Contents: (command-line option, description, image ID for header)
image_list = (
# Images for ATF
("psc-bl", "PSC bootloader image", 36),
("psc-fw", "PSC framework image", 37),
("bl2r-cert", "RIoT Core (BL2R) content certificate", 31),
("bl2r", "RIoT Core (BL2R) bin", 30),
("bl2r-auth-cert", "BL2R AUTHCERT DER-encoded certificate", 42),
("bl2-cert", "Trusted Boot Firmware BL2 certificate", 6),
("bl2", "Trusted Boot Firmware BL2 bin", 1),
("sys", "Running system's part number or PSID", 29),
("bl2-auth-cert", "BL2 AUTHCERT DER-encoded certificate", 43),
("ddr-cert", "DDR Images Content Certificate", 38),
("ddr_ini", "File From Which Will Load DDR (pseudo spd) Parameters", 32),
("snps_images", "Combined SNPS Images for all DIMM types", 33),
("ddr5_snps_images", "DDR5 Combined SNPS Images for all DIMM types", 40),
("ddr_ate_imem", "Analog Test Engine INSTRUCTIONS", 34),
("ddr_ate_dmem", "Analog Test Engine DATA", 35),
("bl30-key-cert", "SCP Firmware BL3-0 key certificate", 8),
("bl30-cert", "SCP Firmware BL3-0 certificate", 12),
("bl30", "SCP Firmware BL3-0", 2),
("psc-app", "PSC application image", 39),
("psc-certs", "PSC attestation certificates", 53),
("trusted-key-cert", "Trusted key certificate", 7),
("bl31-key-cert", "EL3 Runtime Firmware BL3-1 key certificate", 9),
("bl31-cert", "EL3 Runtime Firmware BL3-1 content certificate", 13),
("bl31", "EL3 Runtime Firmware BL3-1 bin", 3),
("bl32-key-cert", "Secure Payload BL3-2 (Trusted OS) key certificate", 10),
("bl32-cert", "Secure Payload BL3-2 (Trusted OS) content certificate", 14),
("bl32", "Secure Payload BL3-2 (Trusted OS) bin", 4),
("bl33-key-cert", "Non-Trusted Firmware BL3-3 key certificate", 11),
("bl33-cert", "Non-Trusted Firmware BL3-3 content certificate", 15),
("bl33", "Non-Trusted Firmware BL3-3 bin", 5),
# Images for UEFI
("capsule", "UEFI capsule image", 52),
("boot-acpi", "Name of the ACPI table", 55),
("boot-dtb", "Name of the dtb file", 56),
("boot-desc", "Default boot menu item description", 57),
("boot-path", "Boot image path", 58),
("boot-args", "Arguments for boot image", 59),
("boot-timeout", "Boot menu timeout", 60),
("uefi-tests", "Specify what UEFI tests to run", 61),
("dummy", "Dummy data for padding", 41),
("ramdisk", "RAM Disk image", 54),
("image", "Boot image", 62),
("initramfs", "In-memory filesystem", 63),
("upgrade-image", "Runtime upgrade image", 51),
)
# Map command-line option to (description, image ID)
image_table = dict([(x, (y, z)) for (x, y, z) in image_list])
# Map image ID to (command-line option, description)
rev_image_table = dict([(z, (x, y)) for (x, y, z) in image_list])
# Did we see the -v option?
verbose = False
# Maximum allowed image version number (inclusive)
MAX_VER = 2
MAJOR_VER = 1
MINOR_VER = 2
#
# We pad images out to an even multiple of 8 bytes.
#
def num_padding_bytes(length):
"""Return the number of padding bytes needed for the given length."""
return ((length + 7) & ~7) - length
class NoHdr(Exception):
"""
Exception raised when we try to create an ImageHdr from a stream at
end-of-file.
"""
pass
class BadHdr(Exception):
"""
Exception raised when we try to create an ImageHdr from a stream
which contains a bad image header.
"""
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class FormatError(Exception):
"""
Exception raised when we encounter a format error in the boot stream.
"""
pass
class CRCMismatch(Exception):
"""
Exception raised when the CRC of an image does not match the CRC
stored in the image header.
"""
pass
#
# Currently we produce/consume a little-endian boot stream. If we
# ever need to change that, you'll need to modify the code below that
# packs/unpacks the header, and possibly the code that handles padding
# bytes at the end of images.
#
class ImageHdr:
"""Represent a BlueField boot stream image header."""
magic = 0x13026642 # Magic number, "Bf^B^S"
default_major = MAJOR_VER # Major version number
default_minor = MINOR_VER # Minor version number
length = 24 # Header length; must be multiple of 8
def __init__(self, infile=None, major=default_major, minor=default_minor,
image_id=0, image_len=0, image_crc=0, following_images=0,
next_img_ver=0, cur_img_ver=0):
if infile:
self.__set_from_file(infile)
else:
self.major = major
self.minor = minor
self.image_id = image_id
self.next_img_ver = next_img_ver
self.cur_img_ver = cur_img_ver
self.image_len = image_len
self.image_crc = image_crc
self.following_images = following_images
def get_header_length(self):
"""Return length of header in bytes."""
#
# Right now this is constant; if later versions increase the header
# size, or include optional fields, this might vary based on the
# specific contents of this header.
#
return length
def get_image_id(self):
"""Return the image's ID."""
return self.image_id
def get_image_length(self):
"""
Return the image's length in bytes. Note that the image following
this header in the boot stream will be padded with the minimum
necessary number of trailing zeroes to make it an even multiple
of 8 bytes long; this padding is not included in the length value.
"""
return self.image_len
def get_image_ver(self):
"""Return the image version of the current image."""
return self.cur_img_ver
def set_next_image_ver(self, version):
"""Set the image version of the next image following this image"""
self.next_img_ver = version
def get_image_crc(self):
"""
Return the image's CRC. Note that this covers both the bytes of
the image, and any added padding bytes.
"""
return self.image_crc
def get_following_images(self):
"""Return bitmap of IDs of subsequent images."""
return self.following_images
def set_following_images(self, follow_map):
"""Set bitmap of IDs of subsequent images."""
self.following_images = follow_map
def get_bits(self):
"""Return a string containing the header."""
w0 = (((self.magic & 0xFFFFFFFF) << 0) |
((self.major & 0xF) << 32) |
((self.minor & 0xF) << 36) |
((self.next_img_ver & 0xF) << 44) |
((self.cur_img_ver & 0xF) << 48) |
(((self.length // 8) & 0xF) << 52) |
((self.image_id & 0xFF) << 56))
w1 = (((self.image_len & 0xFFFFFFFF) << 0) |
((self.image_crc & 0xFFFFFFFF) << 32))
w2 = self.following_images
return struct.pack("<QQQ", w0, w1, w2)
def __set_from_file(self, infile):
"""
Parse the header at the current position of the provided file and
set our state appropriately, consuming only the header bytes;
if the current file position does not contain a valid header,
raise an exception. Note that if an exception is raised, the
amount of data consumed from the file is indeterminate.
"""
#
# This will need to get more complicated if we ever have
# variable-length headers, but for now, we can just read a fixed
# amount.
#
instr = infile.read(self.length)
if not instr:
raise NoHdr()
if len(instr) < self.length:
raise BadHdr("ImageHdr too short")
(w0, w1, w2) = struct.unpack("<QQQ", instr)
magic = w0 & 0xFFFFFFFF
self.major = (w0 >> 32) & 0xF
self.minor = (w0 >> 36) & 0xF
length = (w0 >> 52) & 0xF
self.image_id = (w0 >> 56) & 0xFF
self.image_len = w1 & 0xFFFFFFFF
self.image_crc = (w1 >> 32) & 0xFFFFFFFF
self.following_images = w2
# The next/cur image version fields were only added in minor 2
if self.minor <= 2:
self.next_img_ver = (w0 >> 44) & 0xF
self.cur_img_ver = (w0 >> 48) & 0xF
else:
self.next_img_ver = 0
self.cur_img_ver = 0
if magic != self.magic:
raise BadHdr("Bad ImageHdr magic number 0x%x" % magic)
#
# Obviously the stuff below will change once we support more than
# one version.
#
if self.major != self.default_major:
raise BadHdr("Bad ImageHdr major version %d" % self.major)
if self.minor > self.default_minor:
raise BadHdr("Bad ImageHdr minor version %d" % self.major)
if length != self.length / 8:
raise BadHdr("Bad ImageHdr header length %d" % length)
def dump(self, outfile=sys.stdout):
"""Dump out all internal state."""
outtext = "Ver: %d.%d Len: %d ID: %d " \
"ImLen: %d ImCRC: 0x%x FolIm: 0x%lx" % (
self.major, self.minor, self.length,
self.image_id, self.image_len,
self.image_crc, self.following_images)
if self.minor >= 2:
outtext += " NxImVr: %d ImVr: %d" % (
self.next_img_ver, self.cur_img_ver)
print(outtext, file=outfile)
class Image:
"""Represent a BlueField boot stream image."""
def __init__(self, instream=None, infile=None, idstr=None):
"""
Create an Image object.
instream: File object from which to read the next header and image
data. There may be additional headers, images, or other random
data following that, which will not be consumed.
infile: Name of file from which to read image data. Unlike when
instream is specified, the file does not contain a header, and
the entire file is consumed; a header will be constructed which
describes the data we read. Mutually exclusive with instream.
idstr: String ID (first column of image_list) for image. Required,
and only useful, with infile. Can be suffixed with '-vN' to also
indicate image version number.
"""
if instream:
self.__set_from_stream(instream)
elif infile:
self.__set_from_file(infile, idstr)
else:
self.header = ImageHdr()
self.bits = None
def get_bits(self):
"""Return image data."""
return self.bits
def get_padding(self):
"""Return pad bytes."""
length = self.header.get_image_length()
return b'\0' * num_padding_bytes(length)
def get_image_id(self):
"""Return the numeric image ID for this image."""
return self.header.get_image_id()
def get_image_ver(self):
"""Return the current image ID for this image."""
return self.header.get_image_ver()
def set_next_image_ver(self, next_img_ver):
"""Set the image version of the next image following this image"""
self.header.set_next_image_ver(next_img_ver)
def get_image_length(self):
"""Return the image's length in bytes."""
return self.header.get_image_length()
def set_following_images(self, follow_map):
"""Set the bitmap of images following this one."""
self.header.set_following_images(follow_map)
def write(self, outfile):
"""Write image contents to an output stream."""
outfile.write(self.header.get_bits())
outfile.write(self.bits)
outfile.write(self.get_padding())
def dump(self, outfile=sys.stdout):
self.header.dump(outfile)
def __set_from_stream(self, instream):
"""
Initialize ourselves from the given stream, which contains a header
and then image data; only the data described by the header will be
consumed, so you can call this repeatedly on a stream to pick off
all of the images it contains.
instream: stream to read.
"""
self.header = ImageHdr(infile=instream)
length = self.header.get_image_length()
self.bits = instream.read(length)
if len(self.bits) != length:
raise FormatError("Image ID %d: stream corrupted, image too short" %
self.header.get_image_id())
crc = binascii.crc32(self.bits, 0)
padding = instream.read(num_padding_bytes(length))
crc = binascii.crc32(padding, crc)
crc &= 0xFFFFFFFF
if crc != self.header.get_image_crc():
raise CRCMismatch("Image ID %d: header CRC of 0x%08x does "
"not match calculated CRC of 0x%08x" %
(self.header.get_image_id(),
self.header.get_image_crc(), crc))
def __set_from_file(self, infile, idstr):
"""
Initialize ourselves from the given filename/string. The entire
file or string makes up the image, and we then construct a header
which describes that image data.
stream: file name, or string preceded by "=".
idstr: string ID of image. Can be suffixed with '-vN' to also indicate
image version number.
"""
if infile.startswith("="):
self.bits = bytes(infile[1:], 'utf-8')
else:
with open(infile, "rb") as f:
self.bits = f.read()
length = len(self.bits)
crc = binascii.crc32(self.bits, 0)
crc = binascii.crc32(b'\0' * num_padding_bytes(length), crc)
crc &= 0xFFFFFFFF
# Check if idstr is suffixed with '-vN', while NOT assuming that
# image names can't contain '-v' in its name.
img_ver = 0
if '-' in idstr:
(img, ver) = idstr.rsplit('-', 1)
if len(ver) > 1 and ver[0] == 'v' and ver[1:].isdigit():
if img in image_table:
idstr = img
img_ver = int(ver[1:])
self.header = ImageHdr(image_id=image_table[idstr][1],
image_len=length, image_crc=crc,
cur_img_ver=img_ver)
def make_stream(infnt, outfn, image_tuples, expert):
"""
Write a boot stream to an output file.
infnt: tuple of input file names, files with bigger index takes priority
outfn: output file name.
image_tuples: an iterable containing tuples, each of which contains the
string image identifier (the first column of image_list) and the filename
(or literal string) from the command line.
"""
#
# We'll construct the following_images bitmap while reading in the
# images, and then will reduce it with every output image.
#
fim_map = 0
#
# If there's an input file, open it, and read in its contents.
#
infile_images = {}
for infn in infnt:
infile = open(infn, "rb")
while True:
try:
img = Image(instream=infile)
except NoHdr:
break
id = rev_image_table[img.get_image_id()][0]
if id not in infile_images:
infile_images[id] = {}
infile_images[id][img.get_image_ver()] = img
fim_map |= 1 << img.get_image_id()
infile.close()
#
# If there are files from the command line, open them and read in
# their contents.
#
cmdline_images = {}
# The idver from the command line might have the -vN suffix, so we need
# to generate one without the suffix to be used for expert mode
cmdline_image_order = []
for (idver, fn) in image_tuples:
img = Image(infile=fn, idstr=idver)
id = rev_image_table[img.get_image_id()][0]
if id not in cmdline_images:
cmdline_images[id] = {}
cmdline_images[id][img.get_image_ver()] = img
if id not in cmdline_image_order:
cmdline_image_order.append(id)
fim_map |= 1 << img.get_image_id()
#
# In expert mode, we emit images in the order we found them on the command
# line, but otherwise, we use the order they're found in image_list.
#
if expert:
image_order = cmdline_image_order
else:
image_order = [x[0] for x in image_list]
outfile = open(outfn, "wb")
#
# Emit each image in order, preferring the version from the command line.
#
for i in image_order:
images = {}
# Note the order here will let the images from the cmdline override
# images of the same version read from the file.
if i in infile_images:
images.update(infile_images[i])
if i in cmdline_images:
images.update(cmdline_images[i])
if len(images) == 0:
continue
# Version order is strictly enforced here, even in expert mode.
images = [images[x] for x in sorted(images.keys())]
while len(images) > 0:
img = images.pop(0)
if len(images) == 0:
fim_map &= ~(1 << img.get_image_id())
img.set_next_image_ver(0)
else:
img.set_next_image_ver(images[0].get_image_ver())
img.set_following_images(fim_map)
# The image might be loaded from a previous bfb of a lower version
# so make sure the generated bfb file have the current version.
img.header.minor = MAJOR_VER
img.header.minor = MINOR_VER
img.write(outfile)
outfile.close()
def dump_stream(infn, do_dump, do_extract, prefix, images):
"""
Dump a description of a boot stream.
infn: input filename.
do_dump: if true, print table of contents.
do_extract: if true, extract images to files.
prefix: prefix to use with extracted image filenames.
"""
inf = open(infn, "rb")
inf.seek(0, os.SEEK_END)
file_len = inf.tell()
if file_len < ImageHdr.length:
raise FormatError("BFB too short")
inf.seek(0, os.SEEK_SET)
while True:
try:
img = Image(instream=inf)
except NoHdr:
break
id = img.get_image_id()
if id in rev_image_table:
fn = rev_image_table[id][0]
desc = rev_image_table[id][1]
else:
fn = "image_id_%d" % id
desc = "Unknown image type, ID %d" % id
if img.header.minor >= 2:
fn += "-v%d" % img.get_image_ver()
desc += " (version %d)" % img.get_image_ver()
if do_dump:
if verbose:
img.dump()
else:
print("%10d %s" % (img.get_image_length(), desc))
if do_extract:
if not images or fn in images.split():
xfile = open(prefix + fn, "wb")
xfile.write(img.get_bits())
xfile.close()
inf.close()
# Fix the headers in a list of images so their
# following image bitmap and next version fields are
# correct. Does not enforce any image order.
def fix_image_headers(imgs):
# Figure out the last image of a particular ID
# that appears in the stream. This is repesented by
# the version number, allowing the image to be
# identified by a (ID, Version) pair.
last_images = {}
# In addition, we construct the initial image bitmap here.
fi_map = 0
for img in imgs:
last_images[img.get_image_id()] = img.get_image_ver()
fi_map |= 1 << img.get_image_id()
# Convert to set so we can lookup pairs
last_images = set(last_images.items())
for i, img in enumerate(imgs):
if (img.get_image_id(), img.get_image_ver()) in last_images:
fi_map &= ~(1 << img.get_image_id())
img.set_following_images(fi_map)
# Only following images and next image ver fields
# need to be changed
if i == len(imgs) - 1:
img.set_next_image_ver(0)
else:
img.set_next_image_ver(imgs[i+1].get_image_ver())
img.header.major = MAJOR_VER
img.header.minor = MINOR_VER
FILTER_KEEP = 0
FILTER_STRIP = 1
# Filter a bootstream based on a hardware version.
# On FILTER_KEEP, this function will remove:
# - Images that have a higher version than the one specified.
# - Images that will not execute (such as BF1 images on BF2).
#
# On FILTER_STRIP, this function will remove:
# - Only images that match the strip_ver.
#
# By default this function is "careful". It will attempt to preserve
# at least one image for each given ID, as long as it's compatible with
# the version we're targetting.
def filter_bootstream(
infn,
outfn,
version, # target hw version
filter_type,
strip_ver=None,
careful=True
):
imgs = []
if filter_type == FILTER_STRIP and strip_ver is None:
raise Exception("internal: must specify strip_ver with FILTER_STRIP")
with open(infn, 'rb') as inf:
while True:
try:
inimg = Image(instream=inf)
imgs.append(inimg)
except NoHdr:
break
# Iterate through images and work out what to drop.
# This is a map, ID -> list of versions.
to_drop = {}
# Also keep track of the highest compatible version of image in
# the stream. This is used for careful mode later.
highest_compatible_ver = {}
for img in imgs:
id = img.get_image_id()
ver = img.get_image_ver()
if id not in to_drop:
to_drop[id] = []
# If this ver is "compatible", keep track.
if ver <= version:
highest_compatible_ver[id] = max(
highest_compatible_ver.get(id, 0),
ver
)
if filter_type == FILTER_KEEP:
# Drop everything but specified version
if ver != version:
to_drop[id].append(ver)
elif filter_type == FILTER_STRIP:
# Drop only the version specified
if ver == strip_ver:
to_drop[id].append(ver)
else:
raise Exception("internal: bad filter_type %d" % filter_type)
if careful:
# If we're being careful, don't drop the highest-versioned image
# that is less than or equal to the filter version. This will
# preserve common images, and ensure that there's a (likely)
# compatible image for every ID in the original stream.
for id, ver in highest_compatible_ver.items():
# Remove drop instruction for compat ver
to_drop[id] = [v for v in to_drop[id] if v != ver]
# Filter images.
imgs = [
img for img in imgs
if not img.get_image_ver() in to_drop[img.get_image_id()]
]
# Fix headers and write out.
fix_image_headers(imgs)
with open(outfn, 'wb') as outf:
for img in imgs:
img.write(outf)
def parse_image_opt(option, opt_str, value, parser):
"""Handle one of the --<image-type> options."""
parser.values.images.append((opt_str.lstrip("-"), value))
def main(argv):
#
# Set up our option parser.
#
parser = OptionParser(
usage="\n %prog [ options ] [ INFILE ] [ INFILE2 ] OUTFILE\n"
" %prog -[dxc] [ options ] INFILE\n"
" %prog [ -h | --help ]",
description="%prog creates or dumps a BlueField boot stream.")
parser.add_option(
"-d", "--dump", dest="d", action="store_true",
help="print listing of images within INFILE")
parser.add_option(
"-x", "--extract", dest="x", action="store_true",
help="extract images from INFILE and write to files")
parser.add_option(
"-c", "--check", dest="c", action="store_true",
help="check integrity for images within INFILE")
parser.add_option(
"-n", "--image-name", dest="n", action="store", type="string",
metavar="INM", default="",
help="a string with image names (separated by space) to extract, (used with -x option); ")
parser.add_option(
"-p", "--prefix", dest="p", action="store", type="string",
metavar="PFX", default="dump-",
help="prefix for files containing extracted images; "
"default is '%default'")
parser.add_option(
"-v", "--verbose", dest="v", action="store_true",
help="provide more verbose output")
parser.add_option(
"-e", "--expert", dest="e", action="store_true",
help="expert mode: emit images in order specified on command line")
parser.add_option(
"-f", "--filter", dest="f", action="store", type="int",
metavar="VERSION", default=None,
help="filter away unneeded image versions, targetting the given version")
parser.add_option(
"-s", "--strip", dest="s", action="store", type="int",
metavar="VERSION", default=None,
help="modifies -f, so it only removes a certain version rather than as much as possible")
parser.add_option(
"--no-careful", dest="no_careful", action="store_true",
help=("modifies -f, so it will not attempt to preserve "
"at least one compatible image, instead removing as much "
"as is allowed by the filter"))
parser.set_defaults(images=[])
iog = OptionGroup(parser, "Image Options",
'Images specified on the command line replace images '
'of the same type within the input file. Note: an '
'image specification may either be the name of a file '
'which contains the image data (e.g., --image=vmlinux), '
'or a literal string prefixed with an equals sign '
'(e.g., --boot-args="=debug isolcpus=10").'
'An optional "-vN" can be added to the image name to '
'specify which version the image is, default to 0.')
for fn in image_table:
iog.add_option(
"--" + fn, type="string",
action="callback", callback=parse_image_opt, metavar="IMAGE",
help="take data for " + image_table[fn][0] + " from IMAGE")
# Also add the options with the "-vN" prefix in the image option
for v in range(MAX_VER + 1):
iog.add_option(
"--" + fn + "-v%d" % v, type="string", action="callback",
callback=parse_image_opt, metavar="IMAGE")
parser.add_option_group(iog)
#
# Parse the command line and execute.
#
(opt, args) = parser.parse_args()
if opt.v:
global verbose
verbose = True
if (opt.d or opt.x or opt.c) and len(args) > 1:
parser.error("no output file allowed with -d/-x")
if len(args) > 3:
parser.error("only two input and one output file allowed")
if opt.d or opt.x or opt.f or opt.c:
if opt.e:
parser.error("-e not applicable with -d/-x/-s/-c")
if opt.images:
parser.error("cannot specify images with -d/-x/-s/-c")
if opt.d or opt.x or opt.c:
try:
dump_stream(args[0], opt.d, opt.x, opt.p, opt.n)
except (NoHdr, BadHdr, FormatError, CRCMismatch) as e:
if opt.c:
print("BFB check failed (%s): %s" %
(e.__class__.__name__, e), file=sys.stderr)
sys.exit(1)
else:
raise
elif opt.f is not None:
if len(args) != 2:
parser.error("--filter/-f: must specify exactly one input file and "
"one output file")
infn = args[0]
outfn = args[1]
careful = not opt.no_careful
if opt.s is not None:
filter_bootstream(
infn,
outfn,
opt.f,
FILTER_STRIP,
strip_ver=opt.s,
careful=careful
)
else:
filter_bootstream(
infn,
outfn,
opt.f,
FILTER_KEEP,
careful=careful
)
else:
if len(args) <= 0:
parser.error("must specify a file")
elif len(args) == 1:
infn = ()
outfn = args[0]
if not opt.images:
parser.error("must specify input .bfb or at least one image "
"option with output file")
elif len(args) == 2:
infn = (args[0],)
outfn = args[1]
if opt.e:
parser.error("input file not permitted with -e")
else:
infn = (args[0], args[1])
outfn = args[2]
if opt.e:
parser.error("input file not permitted with -e")
make_stream(infn, outfn, opt.images, opt.e)
if __name__ == "__main__":
main(sys.argv)