blob: b61afa54bc8101442cdbb84e7f97c7790368e3e4 [file] [log] [blame]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +05301#!/usr/bin/env python3
2
3"""Script to create PLDM FW update package"""
4
5import argparse
6import binascii
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +05307import enum
Deepak Kodihalli4ac93442021-04-20 14:44:48 +05308import json
Patrick Williamsc44715d2022-12-08 06:18:18 -06009import math
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053010import os
11import struct
12import sys
Patrick Williamsc44715d2022-12-08 06:18:18 -060013from datetime import datetime
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053014
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053015from bitarray import bitarray
16from bitarray.util import ba2int
17
Patrick Williamsc44715d2022-12-08 06:18:18 -060018string_types = dict(
19 [
20 ("Unknown", 0),
21 ("ASCII", 1),
22 ("UTF8", 2),
23 ("UTF16", 3),
24 ("UTF16LE", 4),
25 ("UTF16BE", 5),
26 ]
27)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053028
Tom Josephda2aaab2021-08-01 19:23:09 -070029initial_descriptor_type_name_length = {
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053030 0x0000: ["PCI Vendor ID", 2],
31 0x0001: ["IANA Enterprise ID", 4],
32 0x0002: ["UUID", 16],
33 0x0003: ["PnP Vendor ID", 3],
Patrick Williamsc44715d2022-12-08 06:18:18 -060034 0x0004: ["ACPI Vendor ID", 4],
35}
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053036
Tom Josephda2aaab2021-08-01 19:23:09 -070037descriptor_type_name_length = {
38 0x0000: ["PCI Vendor ID", 2],
39 0x0001: ["IANA Enterprise ID", 4],
40 0x0002: ["UUID", 16],
41 0x0003: ["PnP Vendor ID", 3],
42 0x0004: ["ACPI Vendor ID", 4],
43 0x0100: ["PCI Device ID", 2],
44 0x0101: ["PCI Subsystem Vendor ID", 2],
45 0x0102: ["PCI Subsystem ID", 2],
46 0x0103: ["PCI Revision ID", 1],
47 0x0104: ["PnP Product Identifier", 4],
Patrick Williamsc44715d2022-12-08 06:18:18 -060048 0x0105: ["ACPI Product Identifier", 4],
49}
Tom Josephda2aaab2021-08-01 19:23:09 -070050
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053051
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +053052class ComponentOptions(enum.IntEnum):
53 """
54 Enum to represent ComponentOptions
55 """
56
57 ForceUpdate = 0
58 UseComponentCompStamp = 1
59
60
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053061def check_string_length(string):
62 """Check if the length of the string is not greater than 255."""
63 if len(string) > 255:
64 sys.exit("ERROR: Max permitted string length is 255")
65
66
67def write_pkg_release_date_time(pldm_fw_up_pkg, release_date_time):
Patrick Williamsc44715d2022-12-08 06:18:18 -060068 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053069 Write the timestamp into the package header. The timestamp is formatted as
70 series of 13 bytes defined in DSP0240 specification.
71
72 Parameters:
73 pldm_fw_up_pkg: PLDM FW update package
74 release_date_time: Package Release Date Time
Patrick Williamsc44715d2022-12-08 06:18:18 -060075 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053076 time = release_date_time.time()
77 date = release_date_time.date()
Patrick Williamsc44715d2022-12-08 06:18:18 -060078 us_bytes = time.microsecond.to_bytes(3, byteorder="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053079 pldm_fw_up_pkg.write(
80 struct.pack(
Patrick Williamsc44715d2022-12-08 06:18:18 -060081 "<hBBBBBBBBHB",
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053082 0,
83 us_bytes[0],
84 us_bytes[1],
85 us_bytes[2],
86 time.second,
87 time.minute,
88 time.hour,
89 date.day,
90 date.month,
91 date.year,
Patrick Williamsc44715d2022-12-08 06:18:18 -060092 0,
93 )
94 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053095
96
97def write_package_version_string(pldm_fw_up_pkg, metadata):
Patrick Williamsc44715d2022-12-08 06:18:18 -060098 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +053099 Write PackageVersionStringType, PackageVersionStringLength and
100 PackageVersionString to the package header.
101
102 Parameters:
103 pldm_fw_up_pkg: PLDM FW update package
104 metadata: metadata about PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600105 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530106 # Hardcoded string type to ASCII
107 string_type = string_types["ASCII"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600108 package_version_string = metadata["PackageHeaderInformation"][
109 "PackageVersionString"
110 ]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530111 check_string_length(package_version_string)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600112 format_string = "<BB" + str(len(package_version_string)) + "s"
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530113 pldm_fw_up_pkg.write(
114 struct.pack(
115 format_string,
116 string_type,
117 len(package_version_string),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600118 package_version_string.encode("ascii"),
119 )
120 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530121
122
123def write_component_bitmap_bit_length(pldm_fw_up_pkg, metadata):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600124 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530125 ComponentBitmapBitLength in the package header indicates the number of bits
126 that will be used represent the bitmap in the ApplicableComponents field
127 for a matching device. The value shall be a multiple of 8 and be large
128 enough to contain a bit for each component in the package. The number of
129 components in the JSON file is used to populate the bitmap length.
130
131 Parameters:
132 pldm_fw_up_pkg: PLDM FW update package
133 metadata: metadata about PLDM FW update package
134
135 Returns:
136 ComponentBitmapBitLength: number of bits that will be used
137 represent the bitmap in the ApplicableComponents field for a
138 matching device
Patrick Williamsc44715d2022-12-08 06:18:18 -0600139 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530140 # The script supports upto 32 components now
141 max_components = 32
142 bitmap_multiple = 8
143
144 num_components = len(metadata["ComponentImageInformationArea"])
145 if num_components > max_components:
146 sys.exit("ERROR: only upto 32 components supported now")
Patrick Williamsc44715d2022-12-08 06:18:18 -0600147 component_bitmap_bit_length = bitmap_multiple * math.ceil(
148 num_components / bitmap_multiple
149 )
150 pldm_fw_up_pkg.write(struct.pack("<H", int(component_bitmap_bit_length)))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530151 return component_bitmap_bit_length
152
153
154def write_pkg_header_info(pldm_fw_up_pkg, metadata):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600155 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530156 ComponentBitmapBitLength in the package header indicates the number of bits
157 that will be used represent the bitmap in the ApplicableComponents field
158 for a matching device. The value shall be a multiple of 8 and be large
159 enough to contain a bit for each component in the package. The number of
160 components in the JSON file is used to populate the bitmap length.
161
162 Parameters:
163 pldm_fw_up_pkg: PLDM FW update package
164 metadata: metadata about PLDM FW update package
165
166 Returns:
167 ComponentBitmapBitLength: number of bits that will be used
168 represent the bitmap in the ApplicableComponents field for a
169 matching device
Patrick Williamsc44715d2022-12-08 06:18:18 -0600170 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530171 uuid = metadata["PackageHeaderInformation"]["PackageHeaderIdentifier"]
172 package_header_identifier = bytearray.fromhex(uuid)
173 pldm_fw_up_pkg.write(package_header_identifier)
174
Patrick Williamsc44715d2022-12-08 06:18:18 -0600175 package_header_format_revision = metadata["PackageHeaderInformation"][
176 "PackageHeaderFormatVersion"
177 ]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530178 # Size will be computed and updated subsequently
179 package_header_size = 0
180 pldm_fw_up_pkg.write(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600181 struct.pack("<BH", package_header_format_revision, package_header_size)
182 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530183
Jeremy Kerrb1475012023-06-04 15:14:36 +0800184 release_date_time_str = metadata["PackageHeaderInformation"].get(
185 "PackageReleaseDateTime", None
186 )
187
188 if release_date_time_str is not None:
Jeremy Kerr99e8b982023-06-04 15:22:54 +0800189 formats = [
190 "%Y-%m-%dT%H:%M:%S",
191 "%Y-%m-%d %H:%M:%S",
192 "%d/%m/%Y %H:%M:%S",
193 ]
194 release_date_time = None
195 for fmt in formats:
196 try:
197 release_date_time = datetime.strptime(
198 release_date_time_str, fmt
199 )
200 break
201 except ValueError:
202 pass
203 if release_date_time is None:
204 sys.exit("Can't parse release date '%s'" % release_date_time_str)
Jeremy Kerrb1475012023-06-04 15:14:36 +0800205 else:
206 release_date_time = datetime.now()
207
208 write_pkg_release_date_time(pldm_fw_up_pkg, release_date_time)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530209
210 component_bitmap_bit_length = write_component_bitmap_bit_length(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600211 pldm_fw_up_pkg, metadata
212 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530213 write_package_version_string(pldm_fw_up_pkg, metadata)
214 return component_bitmap_bit_length
215
216
217def get_applicable_components(device, components, component_bitmap_bit_length):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600218 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530219 This function figures out the components applicable for the device and sets
220 the ApplicableComponents bitfield accordingly.
221
222 Parameters:
223 device: device information
224 components: list of components in the package
225 component_bitmap_bit_length: length of the ComponentBitmapBitLength
226
227 Returns:
228 The ApplicableComponents bitfield
Patrick Williamsc44715d2022-12-08 06:18:18 -0600229 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530230 applicable_components_list = device["ApplicableComponents"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600231 applicable_components = bitarray(
232 component_bitmap_bit_length, endian="little"
233 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530234 applicable_components.setall(0)
Chinmay Shripad Hegdeade5f9b2022-12-20 10:57:55 +0530235 for component_index in applicable_components_list:
236 if 0 <= component_index < len(components):
237 applicable_components[component_index] = 1
238 else:
239 sys.exit("ERROR: Applicable Component index not found.")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530240 return applicable_components
241
242
Tom Josephda2aaab2021-08-01 19:23:09 -0700243def prepare_record_descriptors(descriptors):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600244 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700245 This function processes the Descriptors and prepares the RecordDescriptors
246 section of the the firmware device ID record.
247
248 Parameters:
249 descriptors: Descriptors entry
250
251 Returns:
252 RecordDescriptors, DescriptorCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600253 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700254 record_descriptors = bytearray()
255 vendor_defined_desc_type = 65535
256 vendor_desc_title_str_type_len = 1
257 vendor_desc_title_str_len_len = 1
258 descriptor_count = 0
259
260 for descriptor in descriptors:
Tom Josephda2aaab2021-08-01 19:23:09 -0700261 descriptor_type = descriptor["DescriptorType"]
262 if descriptor_count == 0:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600263 if (
264 initial_descriptor_type_name_length.get(descriptor_type)
265 is None
266 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700267 sys.exit("ERROR: Initial descriptor type not supported")
268 else:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600269 if (
270 descriptor_type_name_length.get(descriptor_type) is None
271 and descriptor_type != vendor_defined_desc_type
272 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700273 sys.exit("ERROR: Descriptor type not supported")
274
275 if descriptor_type == vendor_defined_desc_type:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600276 vendor_desc_title_str = descriptor[
277 "VendorDefinedDescriptorTitleString"
278 ]
Tom Josephda2aaab2021-08-01 19:23:09 -0700279 vendor_desc_data = descriptor["VendorDefinedDescriptorData"]
280 check_string_length(vendor_desc_title_str)
281 vendor_desc_title_str_type = string_types["ASCII"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600282 descriptor_length = (
283 vendor_desc_title_str_type_len
284 + vendor_desc_title_str_len_len
285 + len(vendor_desc_title_str)
286 + len(bytearray.fromhex(vendor_desc_data))
287 )
288 format_string = "<HHBB" + str(len(vendor_desc_title_str)) + "s"
289 record_descriptors.extend(
290 struct.pack(
291 format_string,
292 descriptor_type,
293 descriptor_length,
294 vendor_desc_title_str_type,
295 len(vendor_desc_title_str),
296 vendor_desc_title_str.encode("ascii"),
297 )
298 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700299 record_descriptors.extend(bytearray.fromhex(vendor_desc_data))
300 descriptor_count += 1
301 else:
302 descriptor_type = descriptor["DescriptorType"]
303 descriptor_data = descriptor["DescriptorData"]
304 descriptor_length = len(bytearray.fromhex(descriptor_data))
Patrick Williamsc44715d2022-12-08 06:18:18 -0600305 if (
306 descriptor_length
307 != descriptor_type_name_length.get(descriptor_type)[1]
308 ):
309 err_string = (
310 "ERROR: Descriptor type - "
311 + descriptor_type_name_length.get(descriptor_type)[0]
312 + " length is incorrect"
313 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700314 sys.exit(err_string)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600315 format_string = "<HH"
316 record_descriptors.extend(
317 struct.pack(format_string, descriptor_type, descriptor_length)
318 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700319 record_descriptors.extend(bytearray.fromhex(descriptor_data))
320 descriptor_count += 1
321 return record_descriptors, descriptor_count
322
323
Patrick Williamsc44715d2022-12-08 06:18:18 -0600324def write_fw_device_identification_area(
325 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
326):
327 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530328 Write firmware device ID records into the PLDM package header
329
330 This function writes the DeviceIDRecordCount and the
331 FirmwareDeviceIDRecords into the firmware update package by processing the
332 metadata JSON. Currently there is no support for optional
Tom Josephda2aaab2021-08-01 19:23:09 -0700333 FirmwareDevicePackageData.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530334
335 Parameters:
336 pldm_fw_up_pkg: PLDM FW update package
337 metadata: metadata about PLDM FW update package
338 component_bitmap_bit_length: length of the ComponentBitmapBitLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600339 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530340 # The spec limits the number of firmware device ID records to 255
341 max_device_id_record_count = 255
342 devices = metadata["FirmwareDeviceIdentificationArea"]
343 device_id_record_count = len(devices)
344 if device_id_record_count > max_device_id_record_count:
345 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600346 "ERROR: there can be only upto 255 entries in the "
347 " FirmwareDeviceIdentificationArea section"
348 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530349
350 # DeviceIDRecordCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600351 pldm_fw_up_pkg.write(struct.pack("<B", device_id_record_count))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530352
353 for device in devices:
354 # RecordLength size
355 record_length = 2
356
Tom Josephda2aaab2021-08-01 19:23:09 -0700357 # DescriptorCount
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530358 record_length += 1
359
360 # DeviceUpdateOptionFlags
Patrick Williamsc44715d2022-12-08 06:18:18 -0600361 device_update_option_flags = bitarray(32, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530362 device_update_option_flags.setall(0)
363 # Continue component updates after failure
364 supported_device_update_option_flags = [0]
365 for option in device["DeviceUpdateOptionFlags"]:
366 if option not in supported_device_update_option_flags:
367 sys.exit("ERROR: unsupported DeviceUpdateOptionFlag entry")
368 device_update_option_flags[option] = 1
369 record_length += 4
370
371 # ComponentImageSetVersionStringType supports only ASCII for now
372 component_image_set_version_string_type = string_types["ASCII"]
373 record_length += 1
374
375 # ComponentImageSetVersionStringLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600376 component_image_set_version_string = device[
377 "ComponentImageSetVersionString"
378 ]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530379 check_string_length(component_image_set_version_string)
380 record_length += len(component_image_set_version_string)
381 record_length += 1
382
383 # Optional FirmwareDevicePackageData not supported now,
384 # FirmwareDevicePackageDataLength is set to 0x0000
385 fw_device_pkg_data_length = 0
386 record_length += 2
387
388 # ApplicableComponents
389 components = metadata["ComponentImageInformationArea"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600390 applicable_components = get_applicable_components(
391 device, components, component_bitmap_bit_length
392 )
393 applicable_components_bitfield_length = round(
394 len(applicable_components) / 8
395 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530396 record_length += applicable_components_bitfield_length
397
Tom Josephda2aaab2021-08-01 19:23:09 -0700398 # RecordDescriptors
399 descriptors = device["Descriptors"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600400 record_descriptors, descriptor_count = prepare_record_descriptors(
401 descriptors
402 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700403 record_length += len(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530404
Patrick Williamsc44715d2022-12-08 06:18:18 -0600405 format_string = (
406 "<HBIBBH"
407 + str(applicable_components_bitfield_length)
408 + "s"
409 + str(len(component_image_set_version_string))
410 + "s"
411 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530412 pldm_fw_up_pkg.write(
413 struct.pack(
414 format_string,
415 record_length,
416 descriptor_count,
417 ba2int(device_update_option_flags),
418 component_image_set_version_string_type,
419 len(component_image_set_version_string),
420 fw_device_pkg_data_length,
421 applicable_components.tobytes(),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600422 component_image_set_version_string.encode("ascii"),
423 )
424 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700425 pldm_fw_up_pkg.write(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530426
427
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530428def get_component_comparison_stamp(component):
429 """
430 Get component comparison stamp from metadata file.
431
432 This function checks if ComponentOptions field is having value 1. For
433 ComponentOptions 1, ComponentComparisonStamp value from metadata file
434 is used and Default value 0xFFFFFFFF is used for other Component Options.
435
436 Parameters:
437 component: Component image info
438 Returns:
439 component_comparison_stamp: Component Comparison stamp
440 """
441 component_comparison_stamp = 0xFFFFFFFF
442 if (
443 int(ComponentOptions.UseComponentCompStamp)
444 in component["ComponentOptions"]
445 ):
446 # Use FD vendor selected value from metadata file
447 if "ComponentComparisonStamp" not in component.keys():
448 sys.exit(
449 "ERROR: ComponentComparisonStamp is required"
450 " when value '1' is specified in ComponentOptions field"
451 )
452 else:
453 try:
454 tmp_component_cmp_stmp = int(
455 component["ComponentComparisonStamp"], 16
456 )
457 if 0 < tmp_component_cmp_stmp < 0xFFFFFFFF:
458 component_comparison_stamp = tmp_component_cmp_stmp
459 else:
460 sys.exit(
461 "ERROR: Value for ComponentComparisonStamp "
462 " should be [0x01 - 0xFFFFFFFE] when "
463 "ComponentOptions bit is set to"
464 "'1'(UseComponentComparisonStamp)"
465 )
466 except ValueError: # invalid hext format
467 sys.exit("ERROR: Invalid hex for ComponentComparisonStamp")
468 return component_comparison_stamp
469
470
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530471def write_component_image_info_area(pldm_fw_up_pkg, metadata, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600472 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530473 Write component image information area into the PLDM package header
474
475 This function writes the ComponentImageCount and the
476 ComponentImageInformation into the firmware update package by processing
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530477 the metadata JSON.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530478
479 Parameters:
480 pldm_fw_up_pkg: PLDM FW update package
481 metadata: metadata about PLDM FW update package
482 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600483 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530484 components = metadata["ComponentImageInformationArea"]
485 # ComponentImageCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600486 pldm_fw_up_pkg.write(struct.pack("<H", len(components)))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530487 component_location_offsets = []
488 # ComponentLocationOffset position in individual component image
489 # information
490 component_location_offset_pos = 12
491
492 for component in components:
493 # Record the location of the ComponentLocationOffset to be updated
494 # after appending images to the firmware update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600495 component_location_offsets.append(
496 pldm_fw_up_pkg.tell() + component_location_offset_pos
497 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530498
499 # ComponentClassification
500 component_classification = component["ComponentClassification"]
501 if component_classification < 0 or component_classification > 0xFFFF:
502 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600503 "ERROR: ComponentClassification should be [0x0000 - 0xFFFF]"
504 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530505
506 # ComponentIdentifier
507 component_identifier = component["ComponentIdentifier"]
508 if component_identifier < 0 or component_identifier > 0xFFFF:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600509 sys.exit("ERROR: ComponentIdentifier should be [0x0000 - 0xFFFF]")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530510
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530511 # ComponentComparisonStamp
512 component_comparison_stamp = get_component_comparison_stamp(component)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530513
514 # ComponentOptions
Patrick Williamsc44715d2022-12-08 06:18:18 -0600515 component_options = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530516 component_options.setall(0)
Tom Josephd207b592021-10-27 15:33:16 +0530517 supported_component_options = [0, 1, 2]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530518 for option in component["ComponentOptions"]:
519 if option not in supported_component_options:
520 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600521 "ERROR: unsupported ComponentOption in "
522 " ComponentImageInformationArea section"
523 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530524 component_options[option] = 1
525
526 # RequestedComponentActivationMethod
Patrick Williamsc44715d2022-12-08 06:18:18 -0600527 requested_component_activation_method = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530528 requested_component_activation_method.setall(0)
529 supported_requested_component_activation_method = [0, 1, 2, 3, 4, 5]
530 for option in component["RequestedComponentActivationMethod"]:
531 if option not in supported_requested_component_activation_method:
532 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600533 "ERROR: unsupported RequestedComponent "
534 " ActivationMethod entry"
535 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530536 requested_component_activation_method[option] = 1
537
538 # ComponentLocationOffset
539 component_location_offset = 0
540 # ComponentSize
541 component_size = 0
542 # ComponentVersionStringType
543 component_version_string_type = string_types["ASCII"]
544 # ComponentVersionStringlength
545 # ComponentVersionString
546 component_version_string = component["ComponentVersionString"]
547 check_string_length(component_version_string)
548
Patrick Williamsc44715d2022-12-08 06:18:18 -0600549 format_string = "<HHIHHIIBB" + str(len(component_version_string)) + "s"
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530550 pldm_fw_up_pkg.write(
551 struct.pack(
552 format_string,
553 component_classification,
554 component_identifier,
555 component_comparison_stamp,
556 ba2int(component_options),
557 ba2int(requested_component_activation_method),
558 component_location_offset,
559 component_size,
560 component_version_string_type,
561 len(component_version_string),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600562 component_version_string.encode("ascii"),
563 )
564 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530565
566 index = 0
567 pkg_header_checksum_size = 4
568 start_offset = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
569 # Update ComponentLocationOffset and ComponentSize for all the components
570 for offset in component_location_offsets:
571 file_size = os.stat(image_files[index]).st_size
572 pldm_fw_up_pkg.seek(offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600573 pldm_fw_up_pkg.write(struct.pack("<II", start_offset, file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530574 start_offset += file_size
575 index += 1
576 pldm_fw_up_pkg.seek(0, os.SEEK_END)
577
578
579def write_pkg_header_checksum(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600580 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530581 Write PackageHeaderChecksum into the PLDM package header.
582
583 Parameters:
584 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600585 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530586 pldm_fw_up_pkg.seek(0)
587 package_header_checksum = binascii.crc32(pldm_fw_up_pkg.read())
588 pldm_fw_up_pkg.seek(0, os.SEEK_END)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600589 pldm_fw_up_pkg.write(struct.pack("<I", package_header_checksum))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530590
591
592def update_pkg_header_size(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600593 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530594 Update PackageHeader in the PLDM package header. The package header size
595 which is the count of all bytes in the PLDM package header structure is
596 calculated once the package header contents is complete.
597
598 Parameters:
599 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600600 """
Tom Joseph33ac59d2021-07-30 11:09:54 -0700601 pkg_header_checksum_size = 4
602 file_size = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530603 pkg_header_size_offset = 17
604 # Seek past PackageHeaderIdentifier and PackageHeaderFormatRevision
605 pldm_fw_up_pkg.seek(pkg_header_size_offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600606 pldm_fw_up_pkg.write(struct.pack("<H", file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530607 pldm_fw_up_pkg.seek(0, os.SEEK_END)
608
609
610def append_component_images(pldm_fw_up_pkg, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600611 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530612 Append the component images to the firmware update package.
613
614 Parameters:
615 pldm_fw_up_pkg: PLDM FW update package
616 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600617 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530618 for image in image_files:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600619 with open(image, "rb") as file:
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530620 for line in file:
621 pldm_fw_up_pkg.write(line)
622
623
624def main():
625 """Create PLDM FW update (DSP0267) package based on a JSON metadata file"""
626 parser = argparse.ArgumentParser()
Patrick Williamsc44715d2022-12-08 06:18:18 -0600627 parser.add_argument(
628 "pldmfwuppkgname", help="Name of the PLDM FW update package"
629 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530630 parser.add_argument("metadatafile", help="Path of metadata JSON file")
631 parser.add_argument(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600632 "images",
633 nargs="+",
634 help=(
635 "One or more firmware image paths, in the same order as "
636 " ComponentImageInformationArea entries"
637 ),
638 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530639
640 args = parser.parse_args()
641 image_files = args.images
642 with open(args.metadatafile) as file:
643 try:
644 metadata = json.load(file)
645 except ValueError:
646 sys.exit("ERROR: Invalid metadata JSON file")
647
648 # Validate the number of component images
649 if len(image_files) != len(metadata["ComponentImageInformationArea"]):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600650 sys.exit(
651 "ERROR: number of images passed != number of entries "
652 " in ComponentImageInformationArea"
653 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530654
655 try:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600656 with open(args.pldmfwuppkgname, "w+b") as pldm_fw_up_pkg:
657 component_bitmap_bit_length = write_pkg_header_info(
658 pldm_fw_up_pkg, metadata
659 )
660 write_fw_device_identification_area(
661 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
662 )
663 write_component_image_info_area(
664 pldm_fw_up_pkg, metadata, image_files
665 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530666 update_pkg_header_size(pldm_fw_up_pkg)
Tom Joseph33ac59d2021-07-30 11:09:54 -0700667 write_pkg_header_checksum(pldm_fw_up_pkg)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530668 append_component_images(pldm_fw_up_pkg, image_files)
669 pldm_fw_up_pkg.close()
670 except BaseException:
671 pldm_fw_up_pkg.close()
672 os.remove(args.pldmfwuppkgname)
673 raise
674
675
676if __name__ == "__main__":
677 main()