tools: Extend pldm_fwup_pkg_creator

Extend the pldm_fwup_pkg_creator script to handle multiple record
descriptors in the firmware device ID record. The metadata json also
handles vendor defined descriptors.

Tested: Verified this change by using the updated json in the patch
and sample images to create a package. The fields of the package
header were verified for accuracy.

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I9efde5689e551170b46a4e6ee4ea1e5624173420
diff --git a/tools/fw-update/README.md b/tools/fw-update/README.md
index d231a1f..7dff704 100644
--- a/tools/fw-update/README.md
+++ b/tools/fw-update/README.md
@@ -83,17 +83,32 @@
     - ComponentImageSetVersionString: Supported, must be specified in metadata
     file
     - RecordDescriptors:
-        - Initial Descriptor: Supported, must be specified in metadata file.
-        Metatdata file must have key called "InitialDescriptor", which is of
-        type Object:
-            - InitialDescriptorType: Supported, must be specified in metadata
-            file. The supported types are 0x0000(PCI Vendor ID), 0x0001(IANA
-            Enterprise ID), 0x0002 (UUID), 0x0003(PnP Vendor ID), 0x0004( ACPI
-            Vendor ID)
-            - InitialDescriptorLength: Supported, generated by the script
-            - InitialDescriptorData: Supported, must be specified in metadata
-            file as a hex string
-        - Optional Additional Descriptors: Not supported at the moment
+    Metadata JSON must have a key called "Descriptors", which is of type List.
+    Each List entry corresponds to a descriptor. The first entry is considered
+    as the initial descriptor and the type shall be one of the following
+    0x0000(PCI Vendor ID), 0x0001(IANA Enterprise ID), 0x0002(UUID),
+    0x0003(PnP Vendor ID), 0x0004(ACPI Vendor ID). The additional
+    descriptors support initial descriptor types and additionally
+    0x0100(PCI Device ID), 0x0101(PCI Subsystem Vendor ID),
+    0x0102(PCI Subsystem ID), 0x0103(PCI Revision ID),
+    0x0104(PnP Product Identifier), 0x0105(ACPI Product Identifier),
+    0xFFFF(Vendor defined). For descriptor types other than vendor defined the
+    properties expected are DescriptorType and DescriptorData. If the descriptor
+    type is vendor defined, the properties expected are DescriptorType,
+    VendorDefinedDescriptorTitleString and VendorDefinedDescriptorData.
+    See below for details on properties:
+        - DescriptorType: Supported, must be specified in metadata file
+        - DescriptorLength: Supported, generated by the script
+        - DescriptorData: Supported, must be specified in metadata
+        file as a hex string
+        - VendorDefinedDescriptorTitleStringType: Supported - only ASCII at the
+        moment.
+        - VendorDefinedDescriptorTitleStringLength: Supported, generated by the
+        script
+        - VendorDefinedDescriptorTitleString: Supported, must be specified in
+        metadata file
+        - VendorDefinedDescriptorData: Supported, must be specified in metadata
+        file as a hex string
     - FirmwareDevicePackageData: Not supported at the moment
 
 ## Downstream Device Identification Area
diff --git a/tools/fw-update/metadata-example.json b/tools/fw-update/metadata-example.json
index 70751ad..6584ed9 100644
--- a/tools/fw-update/metadata-example.json
+++ b/tools/fw-update/metadata-example.json
@@ -1,7 +1,7 @@
 {
     "PackageHeaderInformation": {
-        "PackageHeaderIdentifier": "1244D2648D7D4718A030FC8A56587D5A",
-        "PackageHeaderFormatVersion": 2,
+        "PackageHeaderIdentifier": "F018878CCB7D49439800A02F059ACA02",
+        "PackageHeaderFormatVersion": 1,
         "PackageReleaseDateTime": "25/12/2021 00:00:00",
         "PackageVersionString": "VersionString1"
     },
@@ -15,10 +15,21 @@
                 100,
                 200
             ],
-            "InitialDescriptor": {
-                "InitialDescriptorType": 2,
-                "InitialDescriptorData": "1244D2648D7D4718A030FC8A56587D5B"
-            }
+            "Descriptors": [
+                {
+                    "DescriptorType": 2,
+                    "DescriptorData": "1244D2648D7D4718A030FC8A56587D5B"
+                },
+                {
+                    "DescriptorType": 1,
+                    "DescriptorData": "47160000"
+                },
+                {
+                    "DescriptorType": 65535,
+                    "VendorDefinedDescriptorTitleString": "OpenBMC",
+                    "VendorDefinedDescriptorData": "1234"
+                }
+            ]
         },
         {
             "DeviceUpdateOptionFlags": [
@@ -29,10 +40,12 @@
                 200,
                 300
             ],
-            "InitialDescriptor": {
-                "InitialDescriptorType": 2,
-                "InitialDescriptorData": "1244D2648D7D4718A030FC8A56587D5C"
-            }
+            "Descriptors": [
+                {
+                    "DescriptorType": 2,
+                    "DescriptorData": "1244D2648D7D4718A030FC8A56587D5C"
+                }
+            ]
         },
         {
             "DeviceUpdateOptionFlags": [
@@ -41,10 +54,12 @@
             "ApplicableComponents": [
                 100
             ],
-            "InitialDescriptor": {
-                "InitialDescriptorType": 2,
-                "InitialDescriptorData": "1244D2648D7D4718A030FC8A56587D5D"
-            }
+            "Descriptors": [
+                {
+                    "DescriptorType": 2,
+                    "DescriptorData": "1244D2648D7D4718A030FC8A56587D5D"
+                }
+            ]
         }
     ],
     "ComponentImageInformationArea": [
diff --git a/tools/fw-update/pldm_fwup_pkg_creator.py b/tools/fw-update/pldm_fwup_pkg_creator.py
index 1273296..5d603b8 100755
--- a/tools/fw-update/pldm_fwup_pkg_creator.py
+++ b/tools/fw-update/pldm_fwup_pkg_creator.py
@@ -22,13 +22,26 @@
     ("UTF16LE", 4),
     ("UTF16BE", 5)])
 
-descriptor_type_name_length = {
+initial_descriptor_type_name_length = {
     0x0000: ["PCI Vendor ID", 2],
     0x0001: ["IANA Enterprise ID", 4],
     0x0002: ["UUID", 16],
     0x0003: ["PnP Vendor ID", 3],
     0x0004: ["ACPI Vendor ID", 4]}
 
+descriptor_type_name_length = {
+    0x0000: ["PCI Vendor ID", 2],
+    0x0001: ["IANA Enterprise ID", 4],
+    0x0002: ["UUID", 16],
+    0x0003: ["PnP Vendor ID", 3],
+    0x0004: ["ACPI Vendor ID", 4],
+    0x0100: ["PCI Device ID", 2],
+    0x0101: ["PCI Subsystem Vendor ID", 2],
+    0x0102: ["PCI Subsystem ID", 2],
+    0x0103: ["PCI Revision ID", 1],
+    0x0104: ["PnP Product Identifier", 4],
+    0x0105: ["ACPI Product Identifier", 4]}
+
 
 def check_string_length(string):
     """Check if the length of the string is not greater than 255."""
@@ -185,6 +198,74 @@
     return applicable_components
 
 
+def prepare_record_descriptors(descriptors):
+    '''
+    This function processes the Descriptors and prepares the RecordDescriptors
+    section of the the firmware device ID record.
+
+        Parameters:
+            descriptors: Descriptors entry
+
+        Returns:
+            RecordDescriptors, DescriptorCount
+    '''
+    record_descriptors = bytearray()
+    vendor_defined_desc_type = 65535
+    vendor_desc_title_str_type_len = 1
+    vendor_desc_title_str_len_len = 1
+    descriptor_count = 0
+
+    for descriptor in descriptors:
+
+        descriptor_type = descriptor["DescriptorType"]
+        if descriptor_count == 0:
+            if initial_descriptor_type_name_length.get(descriptor_type) \
+                    is None:
+                sys.exit("ERROR: Initial descriptor type not supported")
+        else:
+            if descriptor_type_name_length.get(descriptor_type) is None and \
+                    descriptor_type != vendor_defined_desc_type:
+                sys.exit("ERROR: Descriptor type not supported")
+
+        if descriptor_type == vendor_defined_desc_type:
+            vendor_desc_title_str = \
+                descriptor["VendorDefinedDescriptorTitleString"]
+            vendor_desc_data = descriptor["VendorDefinedDescriptorData"]
+            check_string_length(vendor_desc_title_str)
+            vendor_desc_title_str_type = string_types["ASCII"]
+            descriptor_length = vendor_desc_title_str_type_len + \
+                vendor_desc_title_str_len_len + len(vendor_desc_title_str) + \
+                len(bytearray.fromhex(vendor_desc_data))
+            format_string = '<HHBB' + str(len(vendor_desc_title_str)) + 's'
+            record_descriptors.extend(struct.pack(
+                format_string,
+                descriptor_type,
+                descriptor_length,
+                vendor_desc_title_str_type,
+                len(vendor_desc_title_str),
+                vendor_desc_title_str.encode('ascii')))
+            record_descriptors.extend(bytearray.fromhex(vendor_desc_data))
+            descriptor_count += 1
+        else:
+            descriptor_type = descriptor["DescriptorType"]
+            descriptor_data = descriptor["DescriptorData"]
+            descriptor_length = len(bytearray.fromhex(descriptor_data))
+            if descriptor_length != \
+                    descriptor_type_name_length.get(descriptor_type)[1]:
+                err_string = "ERROR: Descriptor type - " + \
+                    descriptor_type_name_length.get(descriptor_type)[0] + \
+                    " length is incorrect"
+                sys.exit(err_string)
+            format_string = '<HH'
+            record_descriptors.extend(struct.pack(
+                format_string,
+                descriptor_type,
+                descriptor_length))
+            record_descriptors.extend(bytearray.fromhex(descriptor_data))
+            descriptor_count += 1
+    return record_descriptors, descriptor_count
+
+
 def write_fw_device_identification_area(pldm_fw_up_pkg, metadata,
                                         component_bitmap_bit_length):
     '''
@@ -193,7 +274,7 @@
     This function writes the DeviceIDRecordCount and the
     FirmwareDeviceIDRecords into the firmware update package by processing the
     metadata JSON. Currently there is no support for optional
-    FirmwareDevicePackageData and for Additional descriptors.
+    FirmwareDevicePackageData.
 
         Parameters:
             pldm_fw_up_pkg: PLDM FW update package
@@ -216,8 +297,7 @@
         # RecordLength size
         record_length = 2
 
-        # Only initial descriptor type supported now
-        descriptor_count = 1
+        # DescriptorCount
         record_length += 1
 
         # DeviceUpdateOptionFlags
@@ -257,32 +337,15 @@
             round(len(applicable_components)/8)
         record_length += applicable_components_bitfield_length
 
-        initial_descriptor = device["InitialDescriptor"]
-        initial_descriptor_type = initial_descriptor["InitialDescriptorType"]
-        initial_descriptor_data = initial_descriptor["InitialDescriptorData"]
-
-        # InitialDescriptorType
-        if descriptor_type_name_length.get(initial_descriptor_type) is None:
-            sys.exit("ERROR: Initial descriptor type not supported")
-        record_length += 2
-
-        # InitialDescriptorLength
-        initial_descriptor_length = \
-            len(bytearray.fromhex(initial_descriptor_data))
-        if initial_descriptor_length != \
-                descriptor_type_name_length.get(initial_descriptor_type)[1]:
-            err_string = "ERROR: Initial descriptor type - " + \
-                descriptor_type_name_length.get(initial_descriptor_type)[0] + \
-                " length is incorrect"
-            sys.exit(err_string)
-        record_length += 2
-
-        # InitialDescriptorData, the byte order in the JSON is retained.
-        record_length += initial_descriptor_length
+        # RecordDescriptors
+        descriptors = device["Descriptors"]
+        record_descriptors, descriptor_count = \
+            prepare_record_descriptors(descriptors)
+        record_length += len(record_descriptors)
 
         format_string = '<HBIBBH' + \
             str(applicable_components_bitfield_length) + 's' + \
-            str(len(component_image_set_version_string)) + 'sHH'
+            str(len(component_image_set_version_string)) + 's'
         pldm_fw_up_pkg.write(
             struct.pack(
                 format_string,
@@ -293,10 +356,8 @@
                 len(component_image_set_version_string),
                 fw_device_pkg_data_length,
                 applicable_components.tobytes(),
-                component_image_set_version_string.encode('ascii'),
-                initial_descriptor_type,
-                initial_descriptor_length))
-        pldm_fw_up_pkg.write(bytearray.fromhex(initial_descriptor_data))
+                component_image_set_version_string.encode('ascii')))
+        pldm_fw_up_pkg.write(record_descriptors)
 
 
 def write_component_image_info_area(pldm_fw_up_pkg, metadata, image_files):