[PATCH v2 01/18] binman: Add support for generating TI Board config binaries

Neha Malcom Francis n-francis at ti.com
Tue Apr 4 14:13:25 CEST 2023


The ti-board-config entry loads and validates a given YAML config file
against a given schema, and generates the board config binary. K3
devices require these generated binaries to be packed into the final
system firmware images.

Signed-off-by: Neha Malcom Francis <n-francis at ti.com>
---
 tools/binman/entries.rst                      |  50 ++++
 tools/binman/etype/ti_board_config.py         | 246 ++++++++++++++++++
 tools/binman/ftest.py                         |  11 +
 tools/binman/test/277_ti_board_cfg.dts        |  11 +
 .../binman/test/278_ti_board_cfg_combined.dts |  25 ++
 tools/binman/test/yaml/config.yaml            |  11 +
 tools/binman/test/yaml/schema.yaml            |  26 ++
 7 files changed, 380 insertions(+)
 create mode 100644 tools/binman/etype/ti_board_config.py
 create mode 100644 tools/binman/test/277_ti_board_cfg.dts
 create mode 100644 tools/binman/test/278_ti_board_cfg_combined.dts
 create mode 100644 tools/binman/test/yaml/config.yaml
 create mode 100644 tools/binman/test/yaml/schema.yaml

diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index b71af801fd..7cfe61dd09 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -2423,3 +2423,53 @@ may be used instead.
 
 
 
+.. _etype_ti_board_config:
+
+Entry: ti-board-config: Texas Instruments board config binary
+-------------------------------------------------------------
+
+Support for generation of TI schema validated board configuration
+binary
+This etype supports generation of two kinds of board configuration
+binaries: singular board config binary as well as combined board config
+binary.
+
+Properties / Entry arguments:
+    - config-file: File containing board configuration data in YAML
+    - schema-file: File containing board configuration YAML schema against
+    which the config file is validated
+
+These above parameters are used only when the generated binary is
+intended to be a single board configuration binary. Example::
+
+/* generate a my-ti-board-config.bin generated from a YAML configuration
+file validated against the schema*/
+my-ti-board-config {
+    ti-board-config {
+        config = "board-config.yaml";
+        schema = "schema.yaml";
+    };
+};
+
+To generate a combined board configuration binary, we pack the
+needed individual binaries into a ti-board-config binary. In this case,
+the available supported subnode names are board-cfg, pm-cfg, sec-cfg and
+rm-cfg. For example::
+
+/* generate a my-combined-ti-board-config.bin packed with a header
+(containing details about the included board config binaries), along
+with the YAML schema validated binaries themselves*/
+my-combined-ti-board-config {
+    ti-board-config {
+        board-cfg {
+            config = "board-cfg.yaml";
+            schema = "schema.yaml";
+        };
+        sec-cfg {
+            config = "sec-cfg.yaml";
+            schema = "schema.yaml";
+        };
+    };
+};
+
+
diff --git a/tools/binman/etype/ti_board_config.py b/tools/binman/etype/ti_board_config.py
new file mode 100644
index 0000000000..0a9be44afc
--- /dev/null
+++ b/tools/binman/etype/ti_board_config.py
@@ -0,0 +1,246 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2022 Texas Instruments Incorporated - https://www.ti.com/
+# Written by Neha Malcom Francis <n-francis at ti.com>
+#
+# Entry-type module for generating schema validated TI board
+# configuration binary
+#
+
+import os
+import struct
+import tempfile
+import yaml
+
+from collections import OrderedDict
+from jsonschema import validate
+from shutil import copyfileobj
+from shutil import rmtree
+
+from binman.entry import Entry
+from binman.etype.section import Entry_section
+from binman.etype.blob_ext import Entry_blob_ext
+from binman.etype.blob_ext_list import Entry_blob_ext_list
+from dtoc import fdt_util
+from u_boot_pylib import tools, tout
+
+BOARDCFG = 0xB
+BOARDCFG_SEC = 0xD
+BOARDCFG_PM = 0xE
+BOARDCFG_RM = 0xC
+BOARDCFG_NUM_ELEMS = 4
+
+class Entry_ti_board_config(Entry_section):
+    """
+    Support for generation of TI schema validated board configuration
+    binary
+    This etype supports generation of two kinds of board configuration
+    binaries: singular board config binary as well as combined board config
+    binary.
+
+    Available parameters are:
+
+    config-file
+        File containing board configuration data in YAML
+
+    schema-file
+        File containing board configuration YAML schema against which the
+        config file is validated
+
+    These above parameters are used only when the generated binary is
+    intended to be a single board configuration binary. Example::
+
+    /* generate a my-ti-board-config.bin generated from a YAML configuration
+    file validated against the schema*/
+    my-ti-board-config {
+        ti-board-config {
+            config = "board-config.yaml";
+            schema = "schema.yaml";
+        };
+    };
+
+    To generate a combined board configuration binary, we pack the
+    needed individual binaries into a ti-board-config binary. In this case,
+    the available supported subnode names are board-cfg, pm-cfg, sec-cfg and
+    rm-cfg. For example::
+
+    /* generate a my-combined-ti-board-config.bin packed with a header
+    (containing details about the included board config binaries), along
+    with the YAML schema validated binaries themselves*/
+    my-combined-ti-board-config {
+        ti-board-config {
+            board-cfg {
+                config = "board-cfg.yaml";
+                schema = "schema.yaml";
+            };
+            sec-cfg {
+                config = "sec-cfg.yaml";
+                schema = "schema.yaml";
+            };
+        }
+    }
+    """
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node)
+
+        self.config_file = None
+        self.schema_file = None
+
+        self._entries = OrderedDict()
+        self._entries_data = OrderedDict()
+        self.num_elems = BOARDCFG_NUM_ELEMS
+        self.fmt = '<HHHBB'
+        self.index = 0
+        self.binary_offset = 0
+        self.sw_rev = 1
+        self.devgrp = 0
+
+    def ReadNode(self):
+        super().ReadNode()
+        self.config_file = fdt_util.GetString(self._node, 'config')
+        self.schema_file = fdt_util.GetString(self._node, 'schema')
+        if self.config_file is None:
+            self.ReadEntries()
+
+    def ReadEntries(self):
+        """Read the subnodes to find out what should go in this image"""
+        num_cfgs = 0
+        for node in self._node.subnodes:
+            if 'type' not in node.props:
+                num_cfgs += 1
+                etype = 'ti-board-config'
+                entry = Entry.Create(self, node, etype)
+                entry.ReadNode()
+                cfg_data = entry.BuildSectionData(True)
+                self._entries[entry.name] = entry
+                self._entries_data[entry.name] = cfg_data
+        self.num_elems = num_cfgs
+
+    def _convert_to_byte_chunk(self, val, data_type):
+        """Convert value into byte array"""
+        size = 0
+        if (data_type == "#/definitions/u8"):
+            size = 1
+        elif (data_type == "#/definitions/u16"):
+            size = 2
+        elif (data_type == "#/definitions/u32"):
+            size = 4
+        else:
+            raise Exception("Data type not present in definitions")
+        if type(val) == int:
+            br = val.to_bytes(size, byteorder="little")
+        return br
+
+    def _compile_yaml(self, schema_yaml, file_yaml):
+        """Convert YAML file into byte array based on YAML schema"""
+        br = bytearray()
+        for key in file_yaml.keys():
+            node = file_yaml[key]
+            node_schema = schema_yaml['properties'][key]
+            node_type = node_schema.get('type')
+            if not 'type' in node_schema:
+                br += self._convert_to_byte_chunk(node,
+                                                  node_schema.get('$ref'))
+            elif node_type == 'object':
+                br += self._compile_yaml(node_schema, node)
+            elif node_type == 'array':
+                for item in node:
+                    if not isinstance(item, dict):
+                        br += self._convert_to_byte_chunk(
+                            item, schema_yaml['properties'][key]['items']["$ref"])
+                    else:
+                        br += self._compile_yaml(node_schema.get('items'), item)
+        return br
+
+    def _generate_binaries(self):
+        """Generate config binary artifacts from the loaded YAML configuration file"""
+        try:
+            cfg_binary = bytearray()
+            for key in self.file_yaml.keys():
+                node = self.file_yaml[key]
+                node_schema = self.schema_yaml['properties'][key]
+                br = self._compile_yaml(node_schema, node)
+                cfg_binary += br
+        except Exception as e:
+            tout.warning("Combined board config binary was not generated properly")
+            cfg_binary = tools.get_bytes(0, 512)
+        return cfg_binary
+
+    def _add_boardcfg(self, bcfgtype, bcfgdata):
+        size = len(bcfgdata)
+        desc = struct.pack(self.fmt, bcfgtype,
+                            self.binary_offset, size, self.devgrp, 0)
+        with open(self.descfile, "ab+") as desc_fh:
+            desc_fh.write(desc)
+        with open(self.bcfgfile, "ab+") as bcfg_fh:
+            bcfg_fh.write(bcfgdata)
+        self.binary_offset += size
+        self.index += 1
+
+    def _finalize(self):
+        try:
+            with open(self.descfile, "rb") as desc_fh:
+                with open(self.bcfgfile, "rb") as bcfg_fh:
+                    with open(self.fh_file, 'ab+') as fh:
+                        desc_fh.seek(0)
+                        bcfg_fh.seek(0)
+                        copyfileobj(desc_fh, fh)
+                        copyfileobj(bcfg_fh, fh)
+            data = tools.read_file(self.fh_file)
+        except Exception as e:
+            tout.warning("Combined board config binary was not generated properly")
+            data = tools.get_bytes(0, 512)
+        rmtree(self.tmpdir)
+        return data
+
+    def BuildSectionData(self, required):
+        if self.config_file is None:
+            self.binary_offset = 0
+            self.tmpdir = tempfile.mkdtemp()
+            self.fh_file = os.path.join(self.tmpdir, "fh")
+            self.descfile = os.path.join(self.tmpdir, "desc")
+            self.bcfgfile = os.path.join(self.tmpdir, "bcfg")
+            try:
+                with open(self.fh_file, 'wb') as f:
+                    t_bytes = f.write(struct.pack(
+                        '<BB', self.num_elems, self.sw_rev))
+                self.binary_offset += t_bytes
+                self.binary_offset += self.num_elems * struct.calcsize(self.fmt)
+            except Exception as e:
+                tout.warning("Combined board config header was not generated properly")
+
+            if 'board-cfg' in self._entries:
+                self._add_boardcfg(BOARDCFG, self._entries_data['board-cfg'])
+
+            if 'sec-cfg' in self._entries:
+                self._add_boardcfg(BOARDCFG_SEC, self._entries_data['sec-cfg'])
+
+            if 'pm-cfg' in self._entries:
+                self._add_boardcfg(BOARDCFG_PM, self._entries_data['pm-cfg'])
+
+            if 'rm-cfg' in self._entries:
+                self._add_boardcfg(BOARDCFG_RM, self._entries_data['rm-cfg'])
+
+            data = self._finalize()
+            return data
+
+        else:
+            with open(self.config_file, 'r') as f:
+                self.file_yaml = yaml.safe_load(f)
+            with open(self.schema_file, 'r') as sch:
+                self.schema_yaml = yaml.safe_load(sch)
+            try:
+                validate(self.file_yaml, self.schema_yaml)
+            except Exception as e:
+                tout.error(f"Schema validation error: {e}")
+
+            data = self._generate_binaries()
+            return data
+
+    def SetImagePos(self, image_pos):
+        Entry.SetImagePos(self, image_pos)
+
+    def SetCalculatedProperties(self):
+        Entry.SetCalculatedProperties(self)
+
+    def CheckEntries(self):
+        Entry.CheckEntries(self)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 43b4f850a6..28b5bd19d5 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -97,6 +97,7 @@ ENV_DATA              = b'var1=1\nvar2="2"'
 PRE_LOAD_MAGIC        = b'UBSH'
 PRE_LOAD_VERSION      = 0x11223344.to_bytes(4, 'big')
 PRE_LOAD_HDR_SIZE     = 0x00001000.to_bytes(4, 'big')
+TI_BOARD_CONFIG_DATA  = b'\x00\x01'
 
 # Subdirectory of the input dir to use to put test FDTs
 TEST_FDT_SUBDIR       = 'fdts'
@@ -6676,6 +6677,16 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
                                 ['fit'])
         self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
 
+    def testTIBoardConfig(self):
+        """Test that a schema validated board config file can be generated"""
+        data = self._DoReadFile('277_ti_board_cfg.dts')
+        self.assertEqual(TI_BOARD_CONFIG_DATA, data)
+
+    def testTIBoardConfig(self):
+        """Test that a schema validated combined board config file can be generated"""
+        data = self._DoReadFile('278_ti_board_cfg_combined.dts')
+        configlen_noheader = TI_BOARD_CONFIG_DATA*4
+        self.assertGreater(data, configlen_noheader)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/277_ti_board_cfg.dts b/tools/binman/test/277_ti_board_cfg.dts
new file mode 100644
index 0000000000..8b1b210d9d
--- /dev/null
+++ b/tools/binman/test/277_ti_board_cfg.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		ti-board-config {
+			config = "tools/binman/test/yaml/config.yaml";
+			schema = "tools/binman/test/yaml/schema.yaml";
+		};
+	};
+};
diff --git a/tools/binman/test/278_ti_board_cfg_combined.dts b/tools/binman/test/278_ti_board_cfg_combined.dts
new file mode 100644
index 0000000000..54cb383d90
--- /dev/null
+++ b/tools/binman/test/278_ti_board_cfg_combined.dts
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		ti-board-config {
+			board-cfg {
+				config = "tools/binman/test/yaml/config.yaml";
+				schema = "tools/binman/test/yaml/schema.yaml";
+			};
+			sec-cfg {
+				config = "tools/binman/test/yaml/config.yaml";
+				schema = "tools/binman/test/yaml/schema.yaml";
+			};
+			rm-cfg {
+				config = "tools/binman/test/yaml/config.yaml";
+				schema = "tools/binman/test/yaml/schema.yaml";
+			};
+			pm-cfg {
+				config = "tools/binman/test/yaml/config.yaml";
+				schema = "tools/binman/test/yaml/schema.yaml";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/yaml/config.yaml b/tools/binman/test/yaml/config.yaml
new file mode 100644
index 0000000000..5b17e78af9
--- /dev/null
+++ b/tools/binman/test/yaml/config.yaml
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
+#
+# Test config
+#
+---
+
+main-branch:
+  small-branch:
+    a: 0x0
+    b: 0x1
diff --git a/tools/binman/test/yaml/schema.yaml b/tools/binman/test/yaml/schema.yaml
new file mode 100644
index 0000000000..44f08bb6b0
--- /dev/null
+++ b/tools/binman/test/yaml/schema.yaml
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
+#
+# Test schema
+#
+---
+
+definitions:
+    u8:
+        type: integer
+        minimum: 0
+        maximum: 0xff
+
+type: object
+properties:
+    main-branch:
+        type: object
+        properties:
+            small-branch:
+                type: object
+                properties:
+                    a:
+                        $ref: "#/definitions/u8"
+                    b:
+                        $ref: "#/definitions/u8"
+
-- 
2.34.1



More information about the U-Boot mailing list