fwupdate-tools: Add support for user specified ComponentComparisonStamp from metadata file.

Currently, ComponentComparisonStamp is not supported in metadata file
and the value is hardcoded in the script. This change will enable to
specify ComponentComparisonStamp in metadata file when ComponentOptions
is set to 1.

Unit Tests:
  1. ComponentOptions values other than 1. verify Default
     ComponentComparisonStamp used
  2. ComponentOptions is 1 and component comparison stamp is missing,
     verify error message
  3. ComponentOptions is 1, ComponentComparisonStamp provided in the
     metadata file. Verify the package with hexdump
  4. Verify error messages for invalid value for ComponentComparisonStamp

Signed-off-by: Chinmay Shripad Hegde <hosmanechinmay@gmail.com>
Change-Id: I772d30ebe5d80e2cdca365ad4025cca774db218d
diff --git a/tools/fw-update/README.md b/tools/fw-update/README.md
index 7736342..32e6de1 100644
--- a/tools/fw-update/README.md
+++ b/tools/fw-update/README.md
@@ -122,20 +122,26 @@
 
 - ComponentImageCount: Supported, generated by the script
 - ComponentImageInformation: which is a list of Individual Component Image
-  Information records. Each such record can have the following properties: -
-  ComponentClassification: Supported, must be specified in metadata file -
-  ComponentIdentifier: Supported, must be specified in metadata file -
-  ComponentComparisonStamp: Not supported. Set to 0xFFFFFFFF by the script -
-  ComponentOptions: Supported, must be specified in metadata file - add each bit
-  that is to be set to 1 to the list "ComponentOptions" - only supported option
-  at the moment is Force Update (0x0) - RequestedComponentActivationMethod:
-  Supported, must be specified in metadata file - add each bit that is to be set
-  to 1 to the list "RequestedComponentActivationMethod" -
-  ComponentLocationOffset: Supported, generated by the script - ComponentSize:
-  Supported, generated by the script - ComponentVersionStringType: Supported -
-  only ASCII at the moment. Generated by the script -
-  ComponentVersionStringLength: Supported, generated by the script -
-  ComponentVersionString: Supported, must be specified in metadata file
+  Information records. Each such record can have the following properties:
+  - ComponentClassification: Supported, must be specified in metadata file
+  - ComponentIdentifier: Supported, must be specified in metadata file
+  - ComponentComparisonStamp: Supported. Must be specified as hexadecimal string
+    value in metadata file if ComponentOptions bit 1 is selected. If the
+    ComponentOptions bit 1 is not set, the ComponentComparisonStamp will be set
+    to the default value of 0xFFFFFFFF.
+  - ComponentOptions: Supported, must be specified in metadata file
+    - add each bit that is to be set to 1 to the list "ComponentOptions"
+    - supported options are Force Update and Use Component Comparison Stamp.
+  - RequestedComponentActivationMethod: Supported, must be specified in metadata
+    file
+    - add each bit that is to be set to 1 to the list
+      "RequestedComponentActivationMethod"
+  - ComponentLocationOffset: Supported, generated by the script
+  - ComponentSize: Supported, generated by the script
+  - ComponentVersionStringType: Supported - only ASCII at the moment. Generated
+    by the script
+  - ComponentVersionStringLength: Supported, generated by the script
+  - ComponentVersionString: Supported, must be specified in metadata file
 
 ## Package Header Checksum
 
diff --git a/tools/fw-update/metadata-example.json b/tools/fw-update/metadata-example.json
index 1e232d9..185f985 100644
--- a/tools/fw-update/metadata-example.json
+++ b/tools/fw-update/metadata-example.json
@@ -53,7 +53,8 @@
         {
             "ComponentClassification": 10,
             "ComponentIdentifier": 100,
-            "ComponentOptions": [],
+            "ComponentOptions": [1],
+            "ComponentComparisonStamp": "0xFFFFFFFE",
             "RequestedComponentActivationMethod": [],
             "ComponentVersionString": "VersionString5"
         },
diff --git a/tools/fw-update/pldm_fwup_pkg_creator.py b/tools/fw-update/pldm_fwup_pkg_creator.py
index ad60bbf..4c3d233 100755
--- a/tools/fw-update/pldm_fwup_pkg_creator.py
+++ b/tools/fw-update/pldm_fwup_pkg_creator.py
@@ -4,6 +4,7 @@
 
 import argparse
 import binascii
+import enum
 import json
 import math
 import os
@@ -48,6 +49,15 @@
 }
 
 
+class ComponentOptions(enum.IntEnum):
+    """
+    Enum to represent ComponentOptions
+    """
+
+    ForceUpdate = 0
+    UseComponentCompStamp = 1
+
+
 def check_string_length(string):
     """Check if the length of the string is not greater than 255."""
     if len(string) > 255:
@@ -396,15 +406,56 @@
         pldm_fw_up_pkg.write(record_descriptors)
 
 
+def get_component_comparison_stamp(component):
+    """
+    Get component comparison stamp from metadata file.
+
+    This function checks if ComponentOptions field is having value 1. For
+    ComponentOptions 1, ComponentComparisonStamp value from metadata file
+    is used and Default value 0xFFFFFFFF is used for other Component Options.
+
+    Parameters:
+        component: Component image info
+    Returns:
+        component_comparison_stamp: Component Comparison stamp
+    """
+    component_comparison_stamp = 0xFFFFFFFF
+    if (
+        int(ComponentOptions.UseComponentCompStamp)
+        in component["ComponentOptions"]
+    ):
+        # Use FD vendor selected value from metadata file
+        if "ComponentComparisonStamp" not in component.keys():
+            sys.exit(
+                "ERROR: ComponentComparisonStamp is required"
+                " when value '1' is specified in ComponentOptions field"
+            )
+        else:
+            try:
+                tmp_component_cmp_stmp = int(
+                    component["ComponentComparisonStamp"], 16
+                )
+                if 0 < tmp_component_cmp_stmp < 0xFFFFFFFF:
+                    component_comparison_stamp = tmp_component_cmp_stmp
+                else:
+                    sys.exit(
+                        "ERROR: Value for ComponentComparisonStamp "
+                        " should be  [0x01 - 0xFFFFFFFE] when "
+                        "ComponentOptions bit is set to"
+                        "'1'(UseComponentComparisonStamp)"
+                    )
+            except ValueError:  # invalid hext format
+                sys.exit("ERROR: Invalid hex for ComponentComparisonStamp")
+    return component_comparison_stamp
+
+
 def write_component_image_info_area(pldm_fw_up_pkg, metadata, image_files):
     """
     Write component image information area into the PLDM package header
 
     This function writes the ComponentImageCount and the
     ComponentImageInformation into the firmware update package by processing
-    the metadata JSON. Currently there is no support for
-    ComponentComparisonStamp field and the component option use component
-    comparison stamp.
+    the metadata JSON.
 
     Parameters:
         pldm_fw_up_pkg: PLDM FW update package
@@ -438,8 +489,8 @@
         if component_identifier < 0 or component_identifier > 0xFFFF:
             sys.exit("ERROR: ComponentIdentifier should be [0x0000 - 0xFFFF]")
 
-        # ComponentComparisonStamp not supported
-        component_comparison_stamp = 0xFFFFFFFF
+        # ComponentComparisonStamp
+        component_comparison_stamp = get_component_comparison_stamp(component)
 
         # ComponentOptions
         component_options = bitarray(16, endian="little")