blob: 42e8a5af7029492dd217949ea6b2a5a434e00559 [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)
Chinmay Shripad Hegdeade5f9b2022-12-20 10:57:55 +0530218 for component_index in applicable_components_list:
219 if 0 <= component_index < len(components):
220 applicable_components[component_index] = 1
221 else:
222 sys.exit("ERROR: Applicable Component index not found.")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530223 return applicable_components
224
225
Tom Josephda2aaab2021-08-01 19:23:09 -0700226def prepare_record_descriptors(descriptors):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600227 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700228 This function processes the Descriptors and prepares the RecordDescriptors
229 section of the the firmware device ID record.
230
231 Parameters:
232 descriptors: Descriptors entry
233
234 Returns:
235 RecordDescriptors, DescriptorCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600236 """
Tom Josephda2aaab2021-08-01 19:23:09 -0700237 record_descriptors = bytearray()
238 vendor_defined_desc_type = 65535
239 vendor_desc_title_str_type_len = 1
240 vendor_desc_title_str_len_len = 1
241 descriptor_count = 0
242
243 for descriptor in descriptors:
Tom Josephda2aaab2021-08-01 19:23:09 -0700244 descriptor_type = descriptor["DescriptorType"]
245 if descriptor_count == 0:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600246 if (
247 initial_descriptor_type_name_length.get(descriptor_type)
248 is None
249 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700250 sys.exit("ERROR: Initial descriptor type not supported")
251 else:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600252 if (
253 descriptor_type_name_length.get(descriptor_type) is None
254 and descriptor_type != vendor_defined_desc_type
255 ):
Tom Josephda2aaab2021-08-01 19:23:09 -0700256 sys.exit("ERROR: Descriptor type not supported")
257
258 if descriptor_type == vendor_defined_desc_type:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600259 vendor_desc_title_str = descriptor[
260 "VendorDefinedDescriptorTitleString"
261 ]
Tom Josephda2aaab2021-08-01 19:23:09 -0700262 vendor_desc_data = descriptor["VendorDefinedDescriptorData"]
263 check_string_length(vendor_desc_title_str)
264 vendor_desc_title_str_type = string_types["ASCII"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600265 descriptor_length = (
266 vendor_desc_title_str_type_len
267 + vendor_desc_title_str_len_len
268 + len(vendor_desc_title_str)
269 + len(bytearray.fromhex(vendor_desc_data))
270 )
271 format_string = "<HHBB" + str(len(vendor_desc_title_str)) + "s"
272 record_descriptors.extend(
273 struct.pack(
274 format_string,
275 descriptor_type,
276 descriptor_length,
277 vendor_desc_title_str_type,
278 len(vendor_desc_title_str),
279 vendor_desc_title_str.encode("ascii"),
280 )
281 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700282 record_descriptors.extend(bytearray.fromhex(vendor_desc_data))
283 descriptor_count += 1
284 else:
285 descriptor_type = descriptor["DescriptorType"]
286 descriptor_data = descriptor["DescriptorData"]
287 descriptor_length = len(bytearray.fromhex(descriptor_data))
Patrick Williamsc44715d2022-12-08 06:18:18 -0600288 if (
289 descriptor_length
290 != descriptor_type_name_length.get(descriptor_type)[1]
291 ):
292 err_string = (
293 "ERROR: Descriptor type - "
294 + descriptor_type_name_length.get(descriptor_type)[0]
295 + " length is incorrect"
296 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700297 sys.exit(err_string)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600298 format_string = "<HH"
299 record_descriptors.extend(
300 struct.pack(format_string, descriptor_type, descriptor_length)
301 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700302 record_descriptors.extend(bytearray.fromhex(descriptor_data))
303 descriptor_count += 1
304 return record_descriptors, descriptor_count
305
306
Patrick Williamsc44715d2022-12-08 06:18:18 -0600307def write_fw_device_identification_area(
308 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
309):
310 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530311 Write firmware device ID records into the PLDM package header
312
313 This function writes the DeviceIDRecordCount and the
314 FirmwareDeviceIDRecords into the firmware update package by processing the
315 metadata JSON. Currently there is no support for optional
Tom Josephda2aaab2021-08-01 19:23:09 -0700316 FirmwareDevicePackageData.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530317
318 Parameters:
319 pldm_fw_up_pkg: PLDM FW update package
320 metadata: metadata about PLDM FW update package
321 component_bitmap_bit_length: length of the ComponentBitmapBitLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600322 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530323 # The spec limits the number of firmware device ID records to 255
324 max_device_id_record_count = 255
325 devices = metadata["FirmwareDeviceIdentificationArea"]
326 device_id_record_count = len(devices)
327 if device_id_record_count > max_device_id_record_count:
328 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600329 "ERROR: there can be only upto 255 entries in the "
330 " FirmwareDeviceIdentificationArea section"
331 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530332
333 # DeviceIDRecordCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600334 pldm_fw_up_pkg.write(struct.pack("<B", device_id_record_count))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530335
336 for device in devices:
337 # RecordLength size
338 record_length = 2
339
Tom Josephda2aaab2021-08-01 19:23:09 -0700340 # DescriptorCount
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530341 record_length += 1
342
343 # DeviceUpdateOptionFlags
Patrick Williamsc44715d2022-12-08 06:18:18 -0600344 device_update_option_flags = bitarray(32, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530345 device_update_option_flags.setall(0)
346 # Continue component updates after failure
347 supported_device_update_option_flags = [0]
348 for option in device["DeviceUpdateOptionFlags"]:
349 if option not in supported_device_update_option_flags:
350 sys.exit("ERROR: unsupported DeviceUpdateOptionFlag entry")
351 device_update_option_flags[option] = 1
352 record_length += 4
353
354 # ComponentImageSetVersionStringType supports only ASCII for now
355 component_image_set_version_string_type = string_types["ASCII"]
356 record_length += 1
357
358 # ComponentImageSetVersionStringLength
Patrick Williamsc44715d2022-12-08 06:18:18 -0600359 component_image_set_version_string = device[
360 "ComponentImageSetVersionString"
361 ]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530362 check_string_length(component_image_set_version_string)
363 record_length += len(component_image_set_version_string)
364 record_length += 1
365
366 # Optional FirmwareDevicePackageData not supported now,
367 # FirmwareDevicePackageDataLength is set to 0x0000
368 fw_device_pkg_data_length = 0
369 record_length += 2
370
371 # ApplicableComponents
372 components = metadata["ComponentImageInformationArea"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600373 applicable_components = get_applicable_components(
374 device, components, component_bitmap_bit_length
375 )
376 applicable_components_bitfield_length = round(
377 len(applicable_components) / 8
378 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530379 record_length += applicable_components_bitfield_length
380
Tom Josephda2aaab2021-08-01 19:23:09 -0700381 # RecordDescriptors
382 descriptors = device["Descriptors"]
Patrick Williamsc44715d2022-12-08 06:18:18 -0600383 record_descriptors, descriptor_count = prepare_record_descriptors(
384 descriptors
385 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700386 record_length += len(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530387
Patrick Williamsc44715d2022-12-08 06:18:18 -0600388 format_string = (
389 "<HBIBBH"
390 + str(applicable_components_bitfield_length)
391 + "s"
392 + str(len(component_image_set_version_string))
393 + "s"
394 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530395 pldm_fw_up_pkg.write(
396 struct.pack(
397 format_string,
398 record_length,
399 descriptor_count,
400 ba2int(device_update_option_flags),
401 component_image_set_version_string_type,
402 len(component_image_set_version_string),
403 fw_device_pkg_data_length,
404 applicable_components.tobytes(),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600405 component_image_set_version_string.encode("ascii"),
406 )
407 )
Tom Josephda2aaab2021-08-01 19:23:09 -0700408 pldm_fw_up_pkg.write(record_descriptors)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530409
410
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530411def get_component_comparison_stamp(component):
412 """
413 Get component comparison stamp from metadata file.
414
415 This function checks if ComponentOptions field is having value 1. For
416 ComponentOptions 1, ComponentComparisonStamp value from metadata file
417 is used and Default value 0xFFFFFFFF is used for other Component Options.
418
419 Parameters:
420 component: Component image info
421 Returns:
422 component_comparison_stamp: Component Comparison stamp
423 """
424 component_comparison_stamp = 0xFFFFFFFF
425 if (
426 int(ComponentOptions.UseComponentCompStamp)
427 in component["ComponentOptions"]
428 ):
429 # Use FD vendor selected value from metadata file
430 if "ComponentComparisonStamp" not in component.keys():
431 sys.exit(
432 "ERROR: ComponentComparisonStamp is required"
433 " when value '1' is specified in ComponentOptions field"
434 )
435 else:
436 try:
437 tmp_component_cmp_stmp = int(
438 component["ComponentComparisonStamp"], 16
439 )
440 if 0 < tmp_component_cmp_stmp < 0xFFFFFFFF:
441 component_comparison_stamp = tmp_component_cmp_stmp
442 else:
443 sys.exit(
444 "ERROR: Value for ComponentComparisonStamp "
445 " should be [0x01 - 0xFFFFFFFE] when "
446 "ComponentOptions bit is set to"
447 "'1'(UseComponentComparisonStamp)"
448 )
449 except ValueError: # invalid hext format
450 sys.exit("ERROR: Invalid hex for ComponentComparisonStamp")
451 return component_comparison_stamp
452
453
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530454def write_component_image_info_area(pldm_fw_up_pkg, metadata, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600455 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530456 Write component image information area into the PLDM package header
457
458 This function writes the ComponentImageCount and the
459 ComponentImageInformation into the firmware update package by processing
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530460 the metadata JSON.
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530461
462 Parameters:
463 pldm_fw_up_pkg: PLDM FW update package
464 metadata: metadata about PLDM FW update package
465 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600466 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530467 components = metadata["ComponentImageInformationArea"]
468 # ComponentImageCount
Patrick Williamsc44715d2022-12-08 06:18:18 -0600469 pldm_fw_up_pkg.write(struct.pack("<H", len(components)))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530470 component_location_offsets = []
471 # ComponentLocationOffset position in individual component image
472 # information
473 component_location_offset_pos = 12
474
475 for component in components:
476 # Record the location of the ComponentLocationOffset to be updated
477 # after appending images to the firmware update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600478 component_location_offsets.append(
479 pldm_fw_up_pkg.tell() + component_location_offset_pos
480 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530481
482 # ComponentClassification
483 component_classification = component["ComponentClassification"]
484 if component_classification < 0 or component_classification > 0xFFFF:
485 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600486 "ERROR: ComponentClassification should be [0x0000 - 0xFFFF]"
487 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530488
489 # ComponentIdentifier
490 component_identifier = component["ComponentIdentifier"]
491 if component_identifier < 0 or component_identifier > 0xFFFF:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600492 sys.exit("ERROR: ComponentIdentifier should be [0x0000 - 0xFFFF]")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530493
Chinmay Shripad Hegde0d8224a2022-12-13 10:56:49 +0530494 # ComponentComparisonStamp
495 component_comparison_stamp = get_component_comparison_stamp(component)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530496
497 # ComponentOptions
Patrick Williamsc44715d2022-12-08 06:18:18 -0600498 component_options = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530499 component_options.setall(0)
Tom Josephd207b592021-10-27 15:33:16 +0530500 supported_component_options = [0, 1, 2]
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530501 for option in component["ComponentOptions"]:
502 if option not in supported_component_options:
503 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600504 "ERROR: unsupported ComponentOption in "
505 " ComponentImageInformationArea section"
506 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530507 component_options[option] = 1
508
509 # RequestedComponentActivationMethod
Patrick Williamsc44715d2022-12-08 06:18:18 -0600510 requested_component_activation_method = bitarray(16, endian="little")
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530511 requested_component_activation_method.setall(0)
512 supported_requested_component_activation_method = [0, 1, 2, 3, 4, 5]
513 for option in component["RequestedComponentActivationMethod"]:
514 if option not in supported_requested_component_activation_method:
515 sys.exit(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600516 "ERROR: unsupported RequestedComponent "
517 " ActivationMethod entry"
518 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530519 requested_component_activation_method[option] = 1
520
521 # ComponentLocationOffset
522 component_location_offset = 0
523 # ComponentSize
524 component_size = 0
525 # ComponentVersionStringType
526 component_version_string_type = string_types["ASCII"]
527 # ComponentVersionStringlength
528 # ComponentVersionString
529 component_version_string = component["ComponentVersionString"]
530 check_string_length(component_version_string)
531
Patrick Williamsc44715d2022-12-08 06:18:18 -0600532 format_string = "<HHIHHIIBB" + str(len(component_version_string)) + "s"
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530533 pldm_fw_up_pkg.write(
534 struct.pack(
535 format_string,
536 component_classification,
537 component_identifier,
538 component_comparison_stamp,
539 ba2int(component_options),
540 ba2int(requested_component_activation_method),
541 component_location_offset,
542 component_size,
543 component_version_string_type,
544 len(component_version_string),
Patrick Williamsc44715d2022-12-08 06:18:18 -0600545 component_version_string.encode("ascii"),
546 )
547 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530548
549 index = 0
550 pkg_header_checksum_size = 4
551 start_offset = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
552 # Update ComponentLocationOffset and ComponentSize for all the components
553 for offset in component_location_offsets:
554 file_size = os.stat(image_files[index]).st_size
555 pldm_fw_up_pkg.seek(offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600556 pldm_fw_up_pkg.write(struct.pack("<II", start_offset, file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530557 start_offset += file_size
558 index += 1
559 pldm_fw_up_pkg.seek(0, os.SEEK_END)
560
561
562def write_pkg_header_checksum(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600563 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530564 Write PackageHeaderChecksum into the PLDM package header.
565
566 Parameters:
567 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600568 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530569 pldm_fw_up_pkg.seek(0)
570 package_header_checksum = binascii.crc32(pldm_fw_up_pkg.read())
571 pldm_fw_up_pkg.seek(0, os.SEEK_END)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600572 pldm_fw_up_pkg.write(struct.pack("<I", package_header_checksum))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530573
574
575def update_pkg_header_size(pldm_fw_up_pkg):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600576 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530577 Update PackageHeader in the PLDM package header. The package header size
578 which is the count of all bytes in the PLDM package header structure is
579 calculated once the package header contents is complete.
580
581 Parameters:
582 pldm_fw_up_pkg: PLDM FW update package
Patrick Williamsc44715d2022-12-08 06:18:18 -0600583 """
Tom Joseph33ac59d2021-07-30 11:09:54 -0700584 pkg_header_checksum_size = 4
585 file_size = pldm_fw_up_pkg.tell() + pkg_header_checksum_size
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530586 pkg_header_size_offset = 17
587 # Seek past PackageHeaderIdentifier and PackageHeaderFormatRevision
588 pldm_fw_up_pkg.seek(pkg_header_size_offset)
Patrick Williamsc44715d2022-12-08 06:18:18 -0600589 pldm_fw_up_pkg.write(struct.pack("<H", file_size))
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530590 pldm_fw_up_pkg.seek(0, os.SEEK_END)
591
592
593def append_component_images(pldm_fw_up_pkg, image_files):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600594 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530595 Append the component images to the firmware update package.
596
597 Parameters:
598 pldm_fw_up_pkg: PLDM FW update package
599 image_files: component images
Patrick Williamsc44715d2022-12-08 06:18:18 -0600600 """
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530601 for image in image_files:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600602 with open(image, "rb") as file:
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530603 for line in file:
604 pldm_fw_up_pkg.write(line)
605
606
607def main():
608 """Create PLDM FW update (DSP0267) package based on a JSON metadata file"""
609 parser = argparse.ArgumentParser()
Patrick Williamsc44715d2022-12-08 06:18:18 -0600610 parser.add_argument(
611 "pldmfwuppkgname", help="Name of the PLDM FW update package"
612 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530613 parser.add_argument("metadatafile", help="Path of metadata JSON file")
614 parser.add_argument(
Patrick Williamsc44715d2022-12-08 06:18:18 -0600615 "images",
616 nargs="+",
617 help=(
618 "One or more firmware image paths, in the same order as "
619 " ComponentImageInformationArea entries"
620 ),
621 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530622
623 args = parser.parse_args()
624 image_files = args.images
625 with open(args.metadatafile) as file:
626 try:
627 metadata = json.load(file)
628 except ValueError:
629 sys.exit("ERROR: Invalid metadata JSON file")
630
631 # Validate the number of component images
632 if len(image_files) != len(metadata["ComponentImageInformationArea"]):
Patrick Williamsc44715d2022-12-08 06:18:18 -0600633 sys.exit(
634 "ERROR: number of images passed != number of entries "
635 " in ComponentImageInformationArea"
636 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530637
638 try:
Patrick Williamsc44715d2022-12-08 06:18:18 -0600639 with open(args.pldmfwuppkgname, "w+b") as pldm_fw_up_pkg:
640 component_bitmap_bit_length = write_pkg_header_info(
641 pldm_fw_up_pkg, metadata
642 )
643 write_fw_device_identification_area(
644 pldm_fw_up_pkg, metadata, component_bitmap_bit_length
645 )
646 write_component_image_info_area(
647 pldm_fw_up_pkg, metadata, image_files
648 )
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530649 update_pkg_header_size(pldm_fw_up_pkg)
Tom Joseph33ac59d2021-07-30 11:09:54 -0700650 write_pkg_header_checksum(pldm_fw_up_pkg)
Deepak Kodihalli4ac93442021-04-20 14:44:48 +0530651 append_component_images(pldm_fw_up_pkg, image_files)
652 pldm_fw_up_pkg.close()
653 except BaseException:
654 pldm_fw_up_pkg.close()
655 os.remove(args.pldmfwuppkgname)
656 raise
657
658
659if __name__ == "__main__":
660 main()