blob: 4c3d233561186be82213a583c31a07e6b94ad831 [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
184 try:
185 release_date_time = datetime.strptime(
186 metadata["PackageHeaderInformation"]["PackageReleaseDateTime"],
Patrick Williamsc44715d2022-12-08 06:18:18 -0600187 "%d/%m/%Y %H:%M:%S",
188 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530189 write_pkg_release_date_time(pldm_fw_up_pkg, release_date_time)
190 except KeyError:
191 write_pkg_release_date_time(pldm_fw_up_pkg, datetime.now())
192
193 component_bitmap_bit_length = write_component_bitmap_bit_length(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600194 pldm_fw_up_pkg, metadata
195 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530196 write_package_version_string(pldm_fw_up_pkg, metadata)
197 return component_bitmap_bit_length
198
199
200def get_applicable_components(device, components, component_bitmap_bit_length):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600201 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530202 This function figures out the components applicable for the device and sets
203 the ApplicableComponents bitfield accordingly.
204
205 Parameters:
206 device: device information
207 components: list of components in the package
208 component_bitmap_bit_length: length of the ComponentBitmapBitLength
209
210 Returns:
211 The ApplicableComponents bitfield
Patrick Williamsc44715d2022-12-08 06:18:18 -0600212 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530213 applicable_components_list = device["ApplicableComponents"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600214 applicable_components = bitarray(
215 component_bitmap_bit_length, endian="little"
216 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530217 applicable_components.setall(0)
218 for component in components:
219 if component["ComponentIdentifier"] in applicable_components_list:
220 applicable_components[components.index(component)] = 1
221 return applicable_components
222
223
Tom Josephda2aaab2021-08-01 19:23:09 -0700224def prepare_record_descriptors(descriptors):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600225 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700226 This function processes the Descriptors and prepares the RecordDescriptors
227 section of the the firmware device ID record.
228
229 Parameters:
230 descriptors: Descriptors entry
231
232 Returns:
233 RecordDescriptors, DescriptorCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600234 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700235 record_descriptors = bytearray()
236 vendor_defined_desc_type = 65535
237 vendor_desc_title_str_type_len = 1
238 vendor_desc_title_str_len_len = 1
239 descriptor_count = 0
240
241 for descriptor in descriptors:
Tom Josephda2aaab2021-08-01 19:23:09 -0700242 descriptor_type = descriptor["DescriptorType"]
243 if descriptor_count == 0:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600244 if (
245 initial_descriptor_type_name_length.get(descriptor_type)
246 is None
247 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700248 sys.exit("ERROR: Initial descriptor type not supported")
249 else:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600250 if (
251 descriptor_type_name_length.get(descriptor_type) is None
252 and descriptor_type != vendor_defined_desc_type
253 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700254 sys.exit("ERROR: Descriptor type not supported")
255
256 if descriptor_type == vendor_defined_desc_type:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600257 vendor_desc_title_str = descriptor[
258 "VendorDefinedDescriptorTitleString"
259 ]
Tom Josephda2aaab2021-08-01 19:23:09 -0700260 vendor_desc_data = descriptor["VendorDefinedDescriptorData"]
261 check_string_length(vendor_desc_title_str)
262 vendor_desc_title_str_type = string_types["ASCII"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600263 descriptor_length = (
264 vendor_desc_title_str_type_len
265 + vendor_desc_title_str_len_len
266 + len(vendor_desc_title_str)
267 + len(bytearray.fromhex(vendor_desc_data))
268 )
269 format_string = "<HHBB" + str(len(vendor_desc_title_str)) + "s"
270 record_descriptors.extend(
271 struct.pack(
272 format_string,
273 descriptor_type,
274 descriptor_length,
275 vendor_desc_title_str_type,
276 len(vendor_desc_title_str),
277 vendor_desc_title_str.encode("ascii"),
278 )
279 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700280 record_descriptors.extend(bytearray.fromhex(vendor_desc_data))
281 descriptor_count += 1
282 else:
283 descriptor_type = descriptor["DescriptorType"]
284 descriptor_data = descriptor["DescriptorData"]
285 descriptor_length = len(bytearray.fromhex(descriptor_data))
Patrick Williamsc44715d2022-12-08 06:18:18 -0600286 if (
287 descriptor_length
288 != descriptor_type_name_length.get(descriptor_type)[1]
289 ):
290 err_string = (
291 "ERROR: Descriptor type - "
292 + descriptor_type_name_length.get(descriptor_type)[0]
293 + " length is incorrect"
294 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700295 sys.exit(err_string)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600296 format_string = "<HH"
297 record_descriptors.extend(
298 struct.pack(format_string, descriptor_type, descriptor_length)
299 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700300 record_descriptors.extend(bytearray.fromhex(descriptor_data))
301 descriptor_count += 1
302 return record_descriptors, descriptor_count
303
304
Patrick Williamsc44715d2022-12-08 06:18:18 -0600305def write_fw_device_identification_area(
306 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
307):
308 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530309 Write firmware device ID records into the PLDM package header
310
311 This function writes the DeviceIDRecordCount and the
312 FirmwareDeviceIDRecords into the firmware update package by processing the
313 metadata JSON. Currently there is no support for optional
Tom Josephda2aaab2021-08-01 19:23:09 -0700314 FirmwareDevicePackageData.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530315
316 Parameters:
317 pldm_fw_up_pkg: PLDM FW update package
318 metadata: metadata about PLDM FW update package
319 component_bitmap_bit_length: length of the ComponentBitmapBitLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600320 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530321 # The spec limits the number of firmware device ID records to 255
322 max_device_id_record_count = 255
323 devices = metadata["FirmwareDeviceIdentificationArea"]
324 device_id_record_count = len(devices)
325 if device_id_record_count > max_device_id_record_count:
326 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600327 "ERROR: there can be only upto 255 entries in the "
328 " FirmwareDeviceIdentificationArea section"
329 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530330
331 # DeviceIDRecordCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600332 pldm_fw_up_pkg.write(struct.pack("<B", device_id_record_count))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530333
334 for device in devices:
335 # RecordLength size
336 record_length = 2
337
Tom Josephda2aaab2021-08-01 19:23:09 -0700338 # DescriptorCount
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530339 record_length += 1
340
341 # DeviceUpdateOptionFlags
Patrick Williamsc44715d2022-12-08 06:18:18 -0600342 device_update_option_flags = bitarray(32, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530343 device_update_option_flags.setall(0)
344 # Continue component updates after failure
345 supported_device_update_option_flags = [0]
346 for option in device["DeviceUpdateOptionFlags"]:
347 if option not in supported_device_update_option_flags:
348 sys.exit("ERROR: unsupported DeviceUpdateOptionFlag entry")
349 device_update_option_flags[option] = 1
350 record_length += 4
351
352 # ComponentImageSetVersionStringType supports only ASCII for now
353 component_image_set_version_string_type = string_types["ASCII"]
354 record_length += 1
355
356 # ComponentImageSetVersionStringLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600357 component_image_set_version_string = device[
358 "ComponentImageSetVersionString"
359 ]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530360 check_string_length(component_image_set_version_string)
361 record_length += len(component_image_set_version_string)
362 record_length += 1
363
364 # Optional FirmwareDevicePackageData not supported now,
365 # FirmwareDevicePackageDataLength is set to 0x0000
366 fw_device_pkg_data_length = 0
367 record_length += 2
368
369 # ApplicableComponents
370 components = metadata["ComponentImageInformationArea"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600371 applicable_components = get_applicable_components(
372 device, components, component_bitmap_bit_length
373 )
374 applicable_components_bitfield_length = round(
375 len(applicable_components) / 8
376 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530377 record_length += applicable_components_bitfield_length
378
Tom Josephda2aaab2021-08-01 19:23:09 -0700379 # RecordDescriptors
380 descriptors = device["Descriptors"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600381 record_descriptors, descriptor_count = prepare_record_descriptors(
382 descriptors
383 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700384 record_length += len(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530385
Patrick Williamsc44715d2022-12-08 06:18:18 -0600386 format_string = (
387 "<HBIBBH"
388 + str(applicable_components_bitfield_length)
389 + "s"
390 + str(len(component_image_set_version_string))
391 + "s"
392 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530393 pldm_fw_up_pkg.write(
394 struct.pack(
395 format_string,
396 record_length,
397 descriptor_count,
398 ba2int(device_update_option_flags),
399 component_image_set_version_string_type,
400 len(component_image_set_version_string),
401 fw_device_pkg_data_length,
402 applicable_components.tobytes(),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600403 component_image_set_version_string.encode("ascii"),
404 )
405 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700406 pldm_fw_up_pkg.write(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530407
408
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530409def get_component_comparison_stamp(component):
410 """
411 Get component comparison stamp from metadata file.
412
413 This function checks if ComponentOptions field is having value 1. For
414 ComponentOptions 1, ComponentComparisonStamp value from metadata file
415 is used and Default value 0xFFFFFFFF is used for other Component Options.
416
417 Parameters:
418 component: Component image info
419 Returns:
420 component_comparison_stamp: Component Comparison stamp
421 """
422 component_comparison_stamp = 0xFFFFFFFF
423 if (
424 int(ComponentOptions.UseComponentCompStamp)
425 in component["ComponentOptions"]
426 ):
427 # Use FD vendor selected value from metadata file
428 if "ComponentComparisonStamp" not in component.keys():
429 sys.exit(
430 "ERROR: ComponentComparisonStamp is required"
431 " when value '1' is specified in ComponentOptions field"
432 )
433 else:
434 try:
435 tmp_component_cmp_stmp = int(
436 component["ComponentComparisonStamp"], 16
437 )
438 if 0 < tmp_component_cmp_stmp < 0xFFFFFFFF:
439 component_comparison_stamp = tmp_component_cmp_stmp
440 else:
441 sys.exit(
442 "ERROR: Value for ComponentComparisonStamp "
443 " should be [0x01 - 0xFFFFFFFE] when "
444 "ComponentOptions bit is set to"
445 "'1'(UseComponentComparisonStamp)"
446 )
447 except ValueError: # invalid hext format
448 sys.exit("ERROR: Invalid hex for ComponentComparisonStamp")
449 return component_comparison_stamp
450
451
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530452def write_component_image_info_area(pldm_fw_up_pkg, metadata, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600453 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530454 Write component image information area into the PLDM package header
455
456 This function writes the ComponentImageCount and the
457 ComponentImageInformation into the firmware update package by processing
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530458 the metadata JSON.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530459
460 Parameters:
461 pldm_fw_up_pkg: PLDM FW update package
462 metadata: metadata about PLDM FW update package
463 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600464 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530465 components = metadata["ComponentImageInformationArea"]
466 # ComponentImageCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600467 pldm_fw_up_pkg.write(struct.pack("<H", len(components)))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530468 component_location_offsets = []
469 # ComponentLocationOffset position in individual component image
470 # information
471 component_location_offset_pos = 12
472
473 for component in components:
474 # Record the location of the ComponentLocationOffset to be updated
475 # after appending images to the firmware update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600476 component_location_offsets.append(
477 pldm_fw_up_pkg.tell() + component_location_offset_pos
478 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530479
480 # ComponentClassification
481 component_classification = component["ComponentClassification"]
482 if component_classification < 0 or component_classification > 0xFFFF:
483 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600484 "ERROR: ComponentClassification should be [0x0000 - 0xFFFF]"
485 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530486
487 # ComponentIdentifier
488 component_identifier = component["ComponentIdentifier"]
489 if component_identifier < 0 or component_identifier > 0xFFFF:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600490 sys.exit("ERROR: ComponentIdentifier should be [0x0000 - 0xFFFF]")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530491
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530492 # ComponentComparisonStamp
493 component_comparison_stamp = get_component_comparison_stamp(component)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530494
495 # ComponentOptions
Patrick Williamsc44715d2022-12-08 06:18:18 -0600496 component_options = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530497 component_options.setall(0)
Tom Josephd207b592021-10-27 15:33:16 +0530498 supported_component_options = [0, 1, 2]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530499 for option in component["ComponentOptions"]:
500 if option not in supported_component_options:
501 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600502 "ERROR: unsupported ComponentOption in "
503 " ComponentImageInformationArea section"
504 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530505 component_options[option] = 1
506
507 # RequestedComponentActivationMethod
Patrick Williamsc44715d2022-12-08 06:18:18 -0600508 requested_component_activation_method = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530509 requested_component_activation_method.setall(0)
510 supported_requested_component_activation_method = [0, 1, 2, 3, 4, 5]
511 for option in component["RequestedComponentActivationMethod"]:
512 if option not in supported_requested_component_activation_method:
513 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600514 "ERROR: unsupported RequestedComponent "
515 " ActivationMethod entry"
516 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530517 requested_component_activation_method[option] = 1
518
519 # ComponentLocationOffset
520 component_location_offset = 0
521 # ComponentSize
522 component_size = 0
523 # ComponentVersionStringType
524 component_version_string_type = string_types["ASCII"]
525 # ComponentVersionStringlength
526 # ComponentVersionString
527 component_version_string = component["ComponentVersionString"]
528 check_string_length(component_version_string)
529
Patrick Williamsc44715d2022-12-08 06:18:18 -0600530 format_string = "<HHIHHIIBB" + str(len(component_version_string)) + "s"
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530531 pldm_fw_up_pkg.write(
532 struct.pack(
533 format_string,
534 component_classification,
535 component_identifier,
536 component_comparison_stamp,
537 ba2int(component_options),
538 ba2int(requested_component_activation_method),
539 component_location_offset,
540 component_size,
541 component_version_string_type,
542 len(component_version_string),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600543 component_version_string.encode("ascii"),
544 )
545 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530546
547 index = 0
548 pkg_header_checksum_size = 4
549 start_offset = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
550 # Update ComponentLocationOffset and ComponentSize for all the components
551 for offset in component_location_offsets:
552 file_size = os.stat(image_files[index]).st_size
553 pldm_fw_up_pkg.seek(offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600554 pldm_fw_up_pkg.write(struct.pack("<II", start_offset, file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530555 start_offset += file_size
556 index += 1
557 pldm_fw_up_pkg.seek(0, os.SEEK_END)
558
559
560def write_pkg_header_checksum(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600561 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530562 Write PackageHeaderChecksum into the PLDM package header.
563
564 Parameters:
565 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600566 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530567 pldm_fw_up_pkg.seek(0)
568 package_header_checksum = binascii.crc32(pldm_fw_up_pkg.read())
569 pldm_fw_up_pkg.seek(0, os.SEEK_END)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600570 pldm_fw_up_pkg.write(struct.pack("<I", package_header_checksum))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530571
572
573def update_pkg_header_size(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600574 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530575 Update PackageHeader in the PLDM package header. The package header size
576 which is the count of all bytes in the PLDM package header structure is
577 calculated once the package header contents is complete.
578
579 Parameters:
580 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600581 """
Tom Joseph33ac59d2021-07-30 11:09:54 -0700582 pkg_header_checksum_size = 4
583 file_size = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530584 pkg_header_size_offset = 17
585 # Seek past PackageHeaderIdentifier and PackageHeaderFormatRevision
586 pldm_fw_up_pkg.seek(pkg_header_size_offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600587 pldm_fw_up_pkg.write(struct.pack("<H", file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530588 pldm_fw_up_pkg.seek(0, os.SEEK_END)
589
590
591def append_component_images(pldm_fw_up_pkg, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600592 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530593 Append the component images to the firmware update package.
594
595 Parameters:
596 pldm_fw_up_pkg: PLDM FW update package
597 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600598 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530599 for image in image_files:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600600 with open(image, "rb") as file:
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530601 for line in file:
602 pldm_fw_up_pkg.write(line)
603
604
605def main():
606 """Create PLDM FW update (DSP0267) package based on a JSON metadata file"""
607 parser = argparse.ArgumentParser()
Patrick Williamsc44715d2022-12-08 06:18:18 -0600608 parser.add_argument(
609 "pldmfwuppkgname", help="Name of the PLDM FW update package"
610 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530611 parser.add_argument("metadatafile", help="Path of metadata JSON file")
612 parser.add_argument(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600613 "images",
614 nargs="+",
615 help=(
616 "One or more firmware image paths, in the same order as "
617 " ComponentImageInformationArea entries"
618 ),
619 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530620
621 args = parser.parse_args()
622 image_files = args.images
623 with open(args.metadatafile) as file:
624 try:
625 metadata = json.load(file)
626 except ValueError:
627 sys.exit("ERROR: Invalid metadata JSON file")
628
629 # Validate the number of component images
630 if len(image_files) != len(metadata["ComponentImageInformationArea"]):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600631 sys.exit(
632 "ERROR: number of images passed != number of entries "
633 " in ComponentImageInformationArea"
634 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530635
636 try:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600637 with open(args.pldmfwuppkgname, "w+b") as pldm_fw_up_pkg:
638 component_bitmap_bit_length = write_pkg_header_info(
639 pldm_fw_up_pkg, metadata
640 )
641 write_fw_device_identification_area(
642 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
643 )
644 write_component_image_info_area(
645 pldm_fw_up_pkg, metadata, image_files
646 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530647 update_pkg_header_size(pldm_fw_up_pkg)
Tom Joseph33ac59d2021-07-30 11:09:54 -0700648 write_pkg_header_checksum(pldm_fw_up_pkg)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530649 append_component_images(pldm_fw_up_pkg, image_files)
650 pldm_fw_up_pkg.close()
651 except BaseException:
652 pldm_fw_up_pkg.close()
653 os.remove(args.pldmfwuppkgname)
654 raise
655
656
657if __name__ == "__main__":
658 main()