The boot and load extensions in the x509 certificate are required for
requesting the secure entity (TIFS) to boot a core. These fields are
defined in the binman node for each core that must be booted by TIFS
and must be included when generating the signed certificate.

Add support to parse the boot and load extension properties from the
binman node and populate them into the certificate. If any of the
mandatory properties for an extension are missing, that respective
extension section is NOT added to the certificate.

Add test coverage for the boot and load extension functionality in
ti-secure certificates. The test reads the generated config file and
verifies the sysfw_boot_seq and sysfw_image_load sections contain the
expected values from the DTS. The negative test verifies that partially
specified boot/load extension properties trigger an error.

Signed-off-by: Beleswar Padhi <[email protected]>
Reviewed-by: Simon Glass <[email protected]>
---
v6: Changelog:
1. None

Link to v5:
https://lore.kernel.org/all/[email protected]/

v5: Changelog:
1. Log missing properties and raise exception rather than returning
silently.
2. Squash [PATCH v4 07/11] and [PATCH v4 08/11] into [PATCH v5 4/7]
3. Document properties in function docstring.
4. Update tests to check the load and boot sections contain the expected
values from test DTS. Add negative tests to check it raises an error
when some of the properties are missing.
5. Add R/B tags.

Link to v4:
https://lore.kernel.org/all/[email protected]/

v4: Changelog:
1. None

Link to v3:
https://lore.kernel.org/all/[email protected]/

v3: Changelog:
1. New patch. Add support to sign HSM firmware here in U-Boot.

 tools/binman/btool/openssl.py                 | 49 ++++++++++++--
 tools/binman/etype/ti_secure.py               | 65 +++++++++++++++++++
 tools/binman/etype/x509_cert.py               |  4 +-
 tools/binman/ftest.py                         | 45 +++++++++++++
 .../test/vendor/ti_secure_boot_load_ext.dts   | 23 +++++++
 .../ti_secure_boot_load_ext_partial.dts       | 20 ++++++
 6 files changed, 201 insertions(+), 5 deletions(-)
 create mode 100644 tools/binman/test/vendor/ti_secure_boot_load_ext.dts
 create mode 100644 tools/binman/test/vendor/ti_secure_boot_load_ext_partial.dts

diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py
index b26f087c447..39ea4a51356 100644
--- a/tools/binman/btool/openssl.py
+++ b/tools/binman/btool/openssl.py
@@ -82,7 +82,8 @@ imageSize              = INTEGER:{len(indata)}
         return self.run_cmd(*args)
 
     def x509_cert_sysfw(self, cert_fname, input_fname, key_fname, sw_rev,
-                  config_fname, req_dist_name_dict, firewall_cert_data):
+                  config_fname, req_dist_name_dict, firewall_cert_data,
+                  boot_ext_data, load_ext_data):
         """Create a certificate to be booted by system firmware
 
         Args:
@@ -101,12 +102,52 @@ imageSize              = INTEGER:{len(indata)}
                 extended certificate
               - certificate (str): Extended firewall certificate with
                 the information for the firewall configurations.
+            boot_ext_data (dict):
+              - proc_id (int): The processor ID of core being booted
+              - flags_set (int): The config flags to set for core being booted
+              - flags_clr (int): The config flags to clear for core being 
booted
+              - reset_vector (int): The location of reset vector for core being
+                booted
+            load_ext_data (dict):
+              - dest_addr (int): The address to which image has to be copied
+              - auth_type (int): Contains the host ID for core being booted and
+                how the image is to be copied
 
         Returns:
             str: Tool output
         """
         indata = tools.read_file(input_fname)
         hashval = hashlib.sha512(indata).hexdigest()
+
+        if boot_ext_data is not None:
+            boot_ext = f'''
+[ sysfw_boot_seq ]
+bootCore = INTEGER:{boot_ext_data['proc_id']}
+bootCoreOpts_set = INTEGER:{boot_ext_data['flags_set']}
+bootCoreOpts_clr = INTEGER:{boot_ext_data['flags_clr']}
+resetVec = FORMAT:HEX,OCT:{boot_ext_data['reset_vector']:08x}
+# Reserved for future use
+flagsValid = FORMAT:HEX,OCT:00000000
+rsvd1 = INTEGER:0x00
+rsvd2 = INTEGER:0x00
+rsvd3 = INTEGER:0x00
+'''
+        else:
+            boot_ext = ""
+
+        if load_ext_data is not None:
+            load_ext = f'''
+[ sysfw_image_load ]
+destAddr = FORMAT:HEX,OCT:{load_ext_data['dest_addr']:08x}
+authInPlace = INTEGER:{load_ext_data['auth_type']}
+'''
+        else:
+            load_ext = f'''
+[ sysfw_image_load ]
+destAddr = FORMAT:HEX,OCT:00000000
+authInPlace = INTEGER:{hex(firewall_cert_data['auth_in_place'])}
+'''
+
         with open(config_fname, 'w', encoding='utf-8') as outf:
             print(f'''[ req ]
 distinguished_name     = req_distinguished_name
@@ -138,9 +179,9 @@ shaType                = OID:2.16.840.1.101.3.4.2.3
 shaValue               = FORMAT:HEX,OCT:{hashval}
 imageSize              = INTEGER:{len(indata)}
 
-[ sysfw_image_load ]
-destAddr = FORMAT:HEX,OCT:00000000
-authInPlace = INTEGER:{hex(firewall_cert_data['auth_in_place'])}
+{boot_ext}
+
+{load_ext}
 
 [ firewall ]
 numFirewallRegions = INTEGER:{firewall_cert_data['num_firewalls']}
diff --git a/tools/binman/etype/ti_secure.py b/tools/binman/etype/ti_secure.py
index f6caa0286d9..9573fab088f 100644
--- a/tools/binman/etype/ti_secure.py
+++ b/tools/binman/etype/ti_secure.py
@@ -59,6 +59,33 @@ class Entry_ti_secure(Entry_x509_cert):
               ( 0x02: Move the authenticated binary back to the header )
             - Upper Byte - The Host ID of the core owning the firewall
 
+        - proc-id: (optional) The processor ID of the core being booted.
+          Used in TIFS Boot Extension to identify the target core.
+
+        - flags-set: (optional) Configuration flags to set for the core being
+          booted. Used in TIFS Boot Extension.
+
+        - flags-clr: (optional) Configuration flags to clear for the core being
+          booted. Used in TIFS Boot Extension.
+
+        - reset-vector: (optional) The memory address where the core should
+          start execution after being released from reset. Used in TIFS Boot
+          Extension.
+
+        - dest-addr: (optional) The destination address to which the image
+          should be copied. Used in TIFS Load Extension.
+
+        - auth-type: (optional) This is an integer field that contains two 
pieces
+          of information used in TIFS Load Extension:
+
+            - Upper Byte - The Host ID of the core being booted
+            - Lower Byte - Load type (e.g., 0x00: Copy to dest-addr,
+              0x02: Authentication in place)
+
+        Note: The proc-id, flags-set, flags-clr, reset-vector, dest-addr, and
+        auth-type properties form a group. If any are present, all must be
+        specified together to configure loadable core boot parameters.
+
     Output files:
         - input.<unique_name> - input file passed to openssl
         - config.<unique_name> - input file generated for openssl (which is
@@ -116,6 +143,7 @@ class Entry_ti_secure(Entry_x509_cert):
         if auth_in_place:
             self.firewall_cert_data['auth_in_place'] = auth_in_place
             self.ReadFirewallNode()
+        self.ReadLoadableCoreNode()
         self.sha = fdt_util.GetInt(self._node, 'sha', 512)
         self.req_dist_name = {'C': 'US',
                 'ST': 'TX',
@@ -126,6 +154,43 @@ class Entry_ti_secure(Entry_x509_cert):
                 'emailAddress': '[email protected]'}
         self.debug = fdt_util.GetBool(self._node, 'debug', False)
 
+    def ReadLoadableCoreNode(self):
+        # Map DTS property names (with dashes) to Python dict keys (with 
underscores)
+        boot_ext_props = {
+            'proc-id': 'proc_id',
+            'flags-set': 'flags_set',
+            'flags-clr': 'flags_clr',
+            'reset-vector': 'reset_vector'
+        }
+        load_ext_props = {
+            'dest-addr': 'dest_addr',
+            'auth-type': 'auth_type'
+        }
+
+        self.boot_ext = self.ReadIntProps(boot_ext_props)
+        self.load_ext = self.ReadIntProps(load_ext_props)
+
+    def ReadIntProps(self, props_map):
+        props_dict = {}
+        missing_props = []
+
+        for dts_prop, dict_key in props_map.items():
+            val = fdt_util.GetInt(self._node, dts_prop)
+            if val is None:
+                missing_props.append(dts_prop)
+            else:
+                props_dict[dict_key] = val
+
+        # If all properties are missing, return None
+        if len(missing_props) == len(props_map):
+            return None
+
+        # If some but not all properties are present, raise an error
+        if len(missing_props) > 0:
+            self.Raise(f"Loadable core configuration incomplete. Missing 
properties: {','.join(missing_props)}")
+
+        return props_dict
+
     def ReadFirewallNode(self):
         self.firewall_cert_data['certificate'] = ""
         self.firewall_cert_data['num_firewalls'] = 0
diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py
index b6e8b0b4fb0..b6028f6be84 100644
--- a/tools/binman/etype/x509_cert.py
+++ b/tools/binman/etype/x509_cert.py
@@ -102,7 +102,9 @@ class Entry_x509_cert(Entry_collection):
                 config_fname=config_fname,
                 sw_rev=self.sw_rev,
                 req_dist_name_dict=self.req_dist_name,
-                firewall_cert_data=self.firewall_cert_data)
+                firewall_cert_data=self.firewall_cert_data,
+                boot_ext_data=self.boot_ext,
+                load_ext_data=self.load_ext)
         elif type == 'rom':
             stdout = self.openssl.x509_cert_rom(
                 cert_fname=output_fname,
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index a0e8bde7901..f7c5abdabb0 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -7296,6 +7296,51 @@ fdt         fdtmap                Extract the devicetree 
blob from the fdtmap
                                 entry_args=entry_args)[0]
         self.assertGreater(len(data), len(TI_UNSECURE_DATA))
 
+    def testPackTiSecureBootLoadExt(self):
+        """Test that an image with TI secured binary having boot & load 
extensions can be created"""
+        keyfile = self.TestFile('security/key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        data = self._DoReadFileDtb('vendor/ti_secure_boot_load_ext.dts',
+                                   entry_args=entry_args)[0]
+        self.assertGreater(len(data), len(TI_UNSECURE_DATA))
+
+        # Verify that boot and load extensions are correctly written to config 
file
+        image = control.images['image']
+        entries = image.GetEntries()
+        ti_secure_entry = entries['ti-secure']
+        uniq = ti_secure_entry.GetUniqueName()
+        config_fname = tools.get_output_filename('config.%s' % uniq)
+
+        # Read the generated config file
+        with open(config_fname, 'r') as f:
+            config_data = f.read()
+
+        # Verify boot extension values from the DTS
+        self.assertIn('[ sysfw_boot_seq ]', config_data)
+        self.assertIn('bootCore = INTEGER:32', config_data)  # 0x20 = 32
+        self.assertIn('bootCoreOpts_set = INTEGER:1', config_data)  # 
0x00000001 = 1
+        self.assertIn('bootCoreOpts_clr = INTEGER:2', config_data)  # 
0x00000002 = 2
+        self.assertIn('resetVec = FORMAT:HEX,OCT:70000000', config_data)  # 
0x70000000
+
+        # Verify load extension values from the DTS
+        self.assertIn('[ sysfw_image_load ]', config_data)
+        self.assertIn('destAddr = FORMAT:HEX,OCT:80000000', config_data)  # 
0x80000000
+        self.assertIn('authInPlace = INTEGER:2', config_data)  # 0x02
+
+    def testPackTiSecureBootLoadExtPartial(self):
+        """Test that partially specified boot/load extension properties raise 
an error"""
+        keyfile = self.TestFile('security/key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('vendor/ti_secure_boot_load_ext_partial.dts',
+                               entry_args=entry_args)
+        self.assertIn('Loadable core configuration incomplete', 
str(e.exception))
+        self.assertIn('Missing properties:', str(e.exception))
+
     def testEncryptedNoAlgo(self):
         """Test encrypted node with missing required properties"""
         with self.assertRaises(ValueError) as e:
diff --git a/tools/binman/test/vendor/ti_secure_boot_load_ext.dts 
b/tools/binman/test/vendor/ti_secure_boot_load_ext.dts
new file mode 100644
index 00000000000..226415f39ea
--- /dev/null
+++ b/tools/binman/test/vendor/ti_secure_boot_load_ext.dts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               ti-secure {
+                       content = <&unsecure_binary>;
+                       proc-id = <0x20>;
+                       flags-set = <0x00000001>;
+                       flags-clr = <0x00000002>;
+                       reset-vector = <0x70000000>;
+                       dest-addr = <0x80000000>;
+                       auth-type = <0x02>;
+               };
+               unsecure_binary: blob-ext {
+                       filename = "ti_unsecure.bin";
+               };
+       };
+};
diff --git a/tools/binman/test/vendor/ti_secure_boot_load_ext_partial.dts 
b/tools/binman/test/vendor/ti_secure_boot_load_ext_partial.dts
new file mode 100644
index 00000000000..e90d27cb087
--- /dev/null
+++ b/tools/binman/test/vendor/ti_secure_boot_load_ext_partial.dts
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               ti-secure {
+                       content = <&unsecure_binary>;
+                       proc-id = <0x20>;
+                       flags-set = <0x00000001>;
+                       // Missing: flags-clr, reset-vector, dest-addr, 
auth-type
+               };
+               unsecure_binary: blob-ext {
+                       filename = "ti_unsecure.bin";
+               };
+       };
+};
-- 
2.34.1

Reply via email to