Boot: Add support for dependency description

This commit aims to add the ability to specify and add dependency
TLVs to MCUBoot. An image dependency is a security critical data.
Therefore, the dependency TLVs must also be included in the protected
part of the TLV area.

The dependencies between the Secure and Non-secure images can be
specified at build time with the S_IMAGE_MIN_VER and
NS_IMAGE_MIN_VER defines.

Change-Id: I6a5a3e4d02f5a9d363fde3018fb1cba07b940db8
Co-authored-by: Bence Kaposzta <bence.kaposzta@arm.com>
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/bl2/ext/mcuboot/scripts/imgtool.py b/bl2/ext/mcuboot/scripts/imgtool.py
index 1976f72..3b6e874 100644
--- a/bl2/ext/mcuboot/scripts/imgtool.py
+++ b/bl2/ext/mcuboot/scripts/imgtool.py
@@ -112,7 +112,7 @@
                            pad=pad_size)
     key = keys.load(args.key) if args.key else None
     ram_load_address = macro_parser.evaluate_macro(args.layout, image_load_address_re, 0, 1)
-    img.sign(key, ram_load_address)
+    img.sign(key, ram_load_address, args.dependencies)
 
     if pad_size:
         img.pad_to(pad_size, args.align)
@@ -124,6 +124,30 @@
         'getpub': do_getpub,
         'sign': do_sign, }
 
+
+def get_dependencies(text):
+    if text is not None:
+        versions = []
+        images = re.findall(r"\((\d+)", text)
+        if len(images) == 0:
+            msg = "Image dependency format is invalid: {}".format(text)
+            raise argparse.ArgumentTypeError(msg)
+        raw_versions = re.findall(r",\s*([0-9.+]+)\)", text)
+        if len(images) != len(raw_versions):
+            msg = '''There's a mismatch between the number of dependency images
+            and versions in: {}'''.format(text)
+            raise argparse.ArgumentTypeError(msg)
+        for raw_version in raw_versions:
+            try:
+                versions.append(version.decode_version(raw_version))
+            except ValueError as e:
+                print(e)
+        dependencies = dict()
+        dependencies[image.DEP_IMAGES_KEY] = images
+        dependencies[image.DEP_VERSIONS_KEY] = versions
+        return dependencies
+
+
 def alignment_value(text):
     value = int(text)
     if value not in [1, 2, 4, 8]:
@@ -157,6 +181,9 @@
     sign.add_argument("--align", type=alignment_value, required=True)
     sign.add_argument("-v", "--version", type=version.decode_version,
                       default="0.0.0+0")
+    sign.add_argument("-d", "--dependencies", type=get_dependencies,
+                      required=False, help='''Add dependence on another image,
+                      format: "(<image_ID>,<image_version>), ... "''')
     sign.add_argument("-s", "--security-counter", type=intparse,
                       help='Specify explicitly the security counter value')
     sign.add_argument("-H", "--header-size", type=intparse, required=True)
diff --git a/bl2/ext/mcuboot/scripts/imgtool_lib/image.py b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
index ff137a5..b721fa3 100644
--- a/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
+++ b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
@@ -26,6 +26,8 @@
 TLV_HEADER_SIZE = 4
 PAYLOAD_DIGEST_SIZE = 32  # SHA256 hash
 KEYHASH_SIZE = 32
+DEP_IMAGES_KEY = "images"
+DEP_VERSIONS_KEY = "versions"
 
 # Image header flags.
 IMAGE_F = {
@@ -37,6 +39,7 @@
         'SHA256' : 0x10,
         'RSA2048': 0x20,
         'RSA3072': 0x23,
+        'DEPENDENCY': 0x40,
         'SEC_CNT': 0x50, }
 
 TLV_INFO_SIZE = 4
@@ -112,17 +115,38 @@
             if any(v != 0 and v != b'\000' for v in self.payload[0:self.header_size]):
                 raise Exception("Padding requested, but image does not start with zeros")
 
-    def sign(self, key, ramLoadAddress):
+    def sign(self, key, ramLoadAddress, dependencies=None):
         # Size of the security counter TLV:
         # header ('BBH') + payload ('I') = 8 Bytes
         protected_tlv_size = TLV_INFO_SIZE + 8
 
+        if dependencies is None:
+            dependencies_num = 0
+        else:
+            # Size of a dependency TLV:
+            # header ('BBH') + payload('IBBHI') = 16 Bytes
+            dependencies_num = len(dependencies[DEP_IMAGES_KEY])
+            protected_tlv_size += (dependencies_num * 16)
+
         self.add_header(key, protected_tlv_size, ramLoadAddress)
 
         tlv = TLV()
 
         payload = struct.pack('I', self.security_cnt)
         tlv.add('SEC_CNT', payload)
+
+        if dependencies_num != 0:
+            for i in range(dependencies_num):
+                payload = struct.pack(
+                                '<'+'I'+'BBHI',
+                                int(dependencies[DEP_IMAGES_KEY][i]),
+                                dependencies[DEP_VERSIONS_KEY][i].major,
+                                dependencies[DEP_VERSIONS_KEY][i].minor,
+                                dependencies[DEP_VERSIONS_KEY][i].revision,
+                                dependencies[DEP_VERSIONS_KEY][i].build
+                                )
+                tlv.add('DEPENDENCY', payload)
+
         # Full TLV size needs to be calculated in advance, because the
         # header will be protected as well
         full_size = (TLV_INFO_SIZE + len(tlv.buf) + TLV_HEADER_SIZE
@@ -179,7 +203,7 @@
                 IMAGE_MAGIC,
                 0 if (ramLoadAddress is None) else ramLoadAddress, # LoadAddr
                 self.header_size,
-                protected_tlv_size,  # TLV info header + security counter TLV
+                protected_tlv_size,  # TLV info header + SC TLV (+ DEP. TLVs)
                 len(self.payload) - self.header_size, # ImageSz
                 flags, # Flags
                 self.version.major,