1717"""
1818Parse and print header, TLV area and trailer information of a signed image.
1919"""
20+ import json
2021import os .path
2122import struct
23+ import sys
2224
2325import click
2426import yaml
@@ -75,58 +77,58 @@ def parse_boot_magic(trailer_magic):
7577 return magic
7678
7779
78- def print_in_frame (header_text , content ):
80+ def _human_format_frame (header_text , content ):
7981 sepc = " "
8082 header = "#### " + header_text + sepc
8183 post_header = "#" * (_LINE_LENGTH - len (header ))
82- print ( header + post_header )
83-
84- print ("|" , sepc * (_LINE_LENGTH - 2 ), "|" , sep = " " )
84+ lines = []
85+ lines . append ( header + post_header )
86+ lines . append ("|" + sepc * (_LINE_LENGTH - 2 ) + "| " )
8587 offset = (_LINE_LENGTH - len (content )) // 2
8688 pre = "|" + (sepc * (offset - 1 ))
8789 post = sepc * (_LINE_LENGTH - len (pre ) - len (content ) - 1 ) + "|"
88- print (pre , content , post , sep = "" )
89- print ("|" , sepc * (_LINE_LENGTH - 2 ), "|" , sep = "" )
90- print ("#" * _LINE_LENGTH )
90+ lines .append (pre + content + post )
91+ lines .append ("|" + sepc * (_LINE_LENGTH - 2 ) + "|" )
92+ lines .append ("#" * _LINE_LENGTH )
93+ return "\n " .join (lines )
9194
9295
93- def print_in_row (row_text ):
96+ def _human_format_row (row_text ):
9497 row_text = "#### " + row_text + " "
9598 fill = "#" * (_LINE_LENGTH - len (row_text ))
96- print ( row_text + fill )
99+ return row_text + fill
97100
98101
99- def print_tlv_records (tlv_list ):
102+ def _human_format_tlv_records (tlv_list ):
100103 indent = _LINE_LENGTH // 8
104+ lines = []
101105 for tlv in tlv_list :
102- print (" " * indent , "-" * 45 )
103- tlv_type , tlv_length , tlv_data = tlv .keys ()
106+ lines .append (" " * indent + " " + "-" * 45 )
104107
105- if tlv [tlv_type ] in TLV_TYPES :
106- print (" " * indent , f"{ tlv_type } : { TLV_TYPES [tlv [tlv_type ]]} ({ hex (tlv [tlv_type ])} )" )
107- else :
108- print (" " * indent , "{}: {} ({})" .format (
109- tlv_type , "UNKNOWN" , hex (tlv [tlv_type ])))
110- print (" " * indent , f"{ tlv_length } : " , hex (tlv [tlv_length ]))
111- print (" " * indent , f"{ tlv_data } : " , end = "" )
108+ type_name = tlv ["type_name" ]
109+ type_hex = hex (tlv ["type" ])
110+ lines .append (" " * indent + f" type: { type_name } ({ type_hex } )" )
111+ lines .append (" " * indent + f" len: { hex (tlv ['len' ])} " )
112112
113- for j , data in enumerate (tlv [tlv_data ]):
114- print (f"{ data :#04x} " , end = " " )
115- if ((j + 1 ) % 8 == 0 ) and ((j + 1 ) != len (tlv [tlv_data ])):
116- print ("\n " , end = " " * (indent + 7 ))
117- print ()
113+ data_line = " " * indent + " data: "
114+ for j , data in enumerate (tlv ["data" ]):
115+ data_line += f"{ data :#04x} "
116+ if ((j + 1 ) % 8 == 0 ) and ((j + 1 ) != len (tlv ["data" ])):
117+ lines .append (data_line )
118+ data_line = " " * (indent + 7 )
119+ lines .append (data_line )
120+ return "\n " .join (lines )
118121
119122
120- def dump_imginfo (imgfile , outfile = None , silent = False ):
121- """Parse a signed image binary and print/save the available information ."""
123+ def _read_imginfo (imgfile ):
124+ """Parse a signed image binary and return the image data structure ."""
122125 trailer_magic = None
123126 # set to INVALID by default
124127 swap_size = 0x99
125128 swap_info = 0x99
126129 copy_done = 0x99
127130 image_ok = 0x99
128131 trailer = {}
129- key_field_len = None
130132
131133 ext = os .path .splitext (imgfile )[1 ][1 :].lower ()
132134 try :
@@ -174,7 +176,10 @@ def dump_imginfo(imgfile, outfile=None, silent=False):
174176 tlv_off += image .TLV_INFO_SIZE
175177 tlv_data = b [tlv_off :(tlv_off + tlv_len )]
176178 tlv_area ["tlvs_prot" ].append (
177- {"type" : tlv_type , "len" : tlv_len , "data" : tlv_data })
179+ {"type" : tlv_type ,
180+ "type_name" : TLV_TYPES .get (tlv_type , "UNKNOWN" ),
181+ "len" : tlv_len ,
182+ "data" : tlv_data })
178183 tlv_off += tlv_len
179184
180185 _tlv_head = struct .unpack ('HH' , b [tlv_off :(tlv_off + image .TLV_INFO_SIZE )])
@@ -192,7 +197,8 @@ def dump_imginfo(imgfile, outfile=None, silent=False):
192197 tlv_off += image .TLV_INFO_SIZE
193198 tlv_data = b [tlv_off :(tlv_off + tlv_len )]
194199 tlv_area ["tlvs" ].append (
195- {"type" : tlv_type , "len" : tlv_len , "data" : tlv_data })
200+ {"type" : tlv_type , "type_name" : TLV_TYPES .get (tlv_type , "UNKNOWN" ),
201+ "len" : tlv_len , "data" : tlv_data })
196202 tlv_off += tlv_len
197203
198204 _img_pad_size = len (b ) - tlv_end
@@ -241,26 +247,28 @@ def dump_imginfo(imgfile, outfile=None, silent=False):
241247 # Estimated value of key_field_len is correct if
242248 # BOOT_SWAP_SAVE_ENCTLV is unset
243249 key_field_len = image .align_up (16 , max_align ) * 2
250+ trailer ["key_field_len" ] = key_field_len
244251
245- # Generating output yaml file
246- if outfile is not None :
247- imgdata = {"header" : header ,
248- "tlv_area" : tlv_area ,
249- "trailer" : trailer }
250- with open (outfile , "w" ) as outf :
251- # sort_keys - from pyyaml 5.1
252- yaml .dump (imgdata , outf , sort_keys = False )
252+ imginfo = {
253+ "filename" : os .path .basename (imgfile ),
254+ "header" : header ,
255+ "tlv_area" : tlv_area ,
256+ "trailer" : trailer }
253257
254- ###############################################################################
258+ return imginfo
255259
256- if silent :
257- return
258260
259- print ("Printing content of signed image:" , os .path .basename (imgfile ), "\n " )
261+ def _write_format_human (imginfo , outfile ):
262+ filename = imginfo ["filename" ]
263+ header = imginfo ["header" ]
264+ tlv_area = imginfo ["tlv_area" ]
265+ trailer = imginfo ["trailer" ]
266+
267+ print ("Printing content of signed image:" , filename , "\n " , file = outfile )
260268
261269 # Image header
262270 section_name = "Image header (offset: 0x0)"
263- print_in_row ( section_name )
271+ print ( _human_format_row ( section_name ), file = outfile )
264272 for key , value in header .items ():
265273 if key == "flags" :
266274 if not value :
@@ -276,55 +284,106 @@ def dump_imginfo(imgfile, outfile=None, silent=False):
276284
277285 if not isinstance (value , str ):
278286 value = hex (value )
279- print (key , ":" , " " * (19 - len (key )), value , sep = "" )
280- print ("#" * _LINE_LENGTH )
287+ print (key , ":" , " " * (19 - len (key )), value , sep = "" , file = outfile )
288+ print ("#" * _LINE_LENGTH , file = outfile )
281289
282290 # Image payload
283291 _sectionoff = header ["hdr_size" ]
284292 frame_header_text = f"Payload (offset: { hex (_sectionoff )} )"
285293 frame_content = "FW image (size: {} Bytes)" .format (hex (header ["img_size" ]))
286- print_in_frame ( frame_header_text , frame_content )
294+ print ( _human_format_frame ( frame_header_text , frame_content ), file = outfile )
287295
288296 # TLV area
289297 _sectionoff += header ["img_size" ]
298+ protected_tlv_size = header ["protected_tlv_size" ]
290299 if protected_tlv_size != 0 :
291300 # Protected TLV area
292301 section_name = f"Protected TLV area (offset: { hex (_sectionoff )} )"
293- print_in_row ( section_name )
294- print ("magic: " , hex (tlv_area ["tlv_hdr_prot" ]["magic" ]))
295- print ("area size:" , hex (tlv_area ["tlv_hdr_prot" ]["tlv_tot" ]))
296- print_tlv_records ( tlv_area ["tlvs_prot" ])
297- print ("#" * _LINE_LENGTH )
302+ print ( _human_format_row ( section_name ), file = outfile )
303+ print ("magic: " , hex (tlv_area ["tlv_hdr_prot" ]["magic" ]), file = outfile )
304+ print ("area size:" , hex (tlv_area ["tlv_hdr_prot" ]["tlv_tot" ]), file = outfile )
305+ print ( _human_format_tlv_records ( tlv_area ["tlvs_prot" ]), file = outfile )
306+ print ("#" * _LINE_LENGTH , file = outfile )
298307
299308 _sectionoff += protected_tlv_size
300309 section_name = f"TLV area (offset: { hex (_sectionoff )} )"
301- print_in_row (section_name )
302- print ("magic: " , hex (tlv_area ["tlv_hdr" ]["magic" ]))
303- print ("area size:" , hex (tlv_area ["tlv_hdr" ]["tlv_tot" ]))
304- print_tlv_records (tlv_area ["tlvs" ])
305- print ("#" * _LINE_LENGTH )
306-
307- if _img_pad_size :
310+ print (_human_format_row (section_name ), file = outfile )
311+ print ("magic: " , hex (tlv_area ["tlv_hdr" ]["magic" ]), file = outfile )
312+ print ("area size:" , hex (tlv_area ["tlv_hdr" ]["tlv_tot" ]), file = outfile )
313+ print (_human_format_tlv_records (tlv_area ["tlvs" ]), file = outfile )
314+ print ("#" * _LINE_LENGTH , file = outfile )
315+
316+ # Check if trailer has data (for image padding and trailer info)
317+ if trailer .get ("magic" ):
318+ trailer_magic = trailer ["magic" ]
308319 _sectionoff += tlv_area ["tlv_hdr" ]["tlv_tot" ]
309- _erased_val = b [_sectionoff ]
310- frame_header_text = f"Image padding (offset: { hex (_sectionoff )} )"
311- frame_content = f"padding ({ hex (_erased_val )} )"
312- print_in_frame (frame_header_text , frame_content )
320+ # Note: We don't have access to original binary data here, so skip padding details
313321
314322 # Image trailer
315323 section_name = "Image trailer (offset: unknown)"
316- print_in_row ( section_name )
324+ print ( _human_format_row ( section_name ), file = outfile )
317325 notice = "(Note: some fields may not be used, depending on the update strategy)\n "
318326 notice = '\n ' .join (notice [i :i + _LINE_LENGTH ] for i in range (0 , len (notice ), _LINE_LENGTH ))
319- print (notice )
320- print ("swap status: (len: unknown)" )
321- print ("enc. keys: " , parse_enc (key_field_len ))
322- print ("swap size: " , parse_size (hex (swap_size )))
323- print ("swap_info: " , parse_status (hex (swap_info )))
324- print ("copy_done: " , parse_status (hex (copy_done )))
325- print ("image_ok: " , parse_status (hex (image_ok )))
326- print ("boot magic: " , parse_boot_magic (trailer_magic ))
327- print ()
327+ print (notice , file = outfile )
328+ print ("swap status: (len: unknown)" , file = outfile )
329+ print ("enc. keys: " , parse_enc (trailer .get ("key_field_len" )), file = outfile )
330+
331+ # Only print trailer fields if they exist
332+ if "swap_size" in trailer :
333+ print ("swap size: " , parse_size (hex (trailer ["swap_size" ])), file = outfile )
334+ if "swap_info" in trailer :
335+ print ("swap_info: " , parse_status (hex (trailer ["swap_info" ])), file = outfile )
336+ if "copy_done" in trailer :
337+ print ("copy_done: " , parse_status (hex (trailer ["copy_done" ])), file = outfile )
338+ if "image_ok" in trailer :
339+ print ("image_ok: " , parse_status (hex (trailer ["image_ok" ])), file = outfile )
340+ print ("boot magic: " , parse_boot_magic (trailer_magic ), file = outfile )
341+ print (file = outfile )
328342
329343 footer = "End of Image "
330- print_in_row (footer )
344+ print (_human_format_row (footer ), file = outfile )
345+
346+ def _json_default_serializer (obj ):
347+ """Convert non-JSON-serializable objects to JSON-serializable format."""
348+ if isinstance (obj , (bytes , bytearray )):
349+ return obj .hex ()
350+ raise TypeError (f"Object of type { type (obj ).__name__ } is not JSON serializable" )
351+
352+
353+ def _write_format (imginfo , output_format , out ):
354+ """Write image info in the specified format to the output stream."""
355+ if output_format == 'human' :
356+ _write_format_human (imginfo , out )
357+
358+ elif output_format == 'yaml' :
359+ yaml .dump (imginfo , out , sort_keys = False )
360+
361+ elif output_format == 'json' :
362+ json .dump (imginfo , out , indent = 2 , default = _json_default_serializer )
363+ if out == sys .stdout :
364+ print () # Add newline after JSON output to stdout
365+ else :
366+ raise ValueError (f"Invalid output format: { output_format } " )
367+
368+
369+ def dump_imginfo (imgfile , outfile = None , output_format = None , silent = False ):
370+ """Parse a signed image binary and print/save the available information."""
371+
372+ # Note: silent parameter is kept for backward compatibility but is ignored.
373+ # The function's purpose is to output data, so silent doesn't make sense here.
374+
375+ # Determine output format based on backward compatibility rules
376+ if output_format is None :
377+ if outfile is None :
378+ output_format = 'human' # no --outfile defaults to human-friendly
379+ else :
380+ output_format = 'yaml' # --outfile without --format defaults to yaml
381+
382+ imginfo = _read_imginfo (imgfile )
383+
384+ if outfile :
385+ with open (outfile , "w" ) as out :
386+ _write_format (imginfo , output_format , out )
387+ else :
388+ _write_format (imginfo , output_format , sys .stdout )
389+
0 commit comments