binman: ti-board-config: Add support for TI board config binaries
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 binaries to be packed into the final system
firmware images.
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Neha Malcom Francis <n-francis@ti.com>
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index b55f424..8a9e778 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -1664,6 +1664,54 @@
+.. _etype_ti_board_config:
+
+Entry: ti-board-config: An entry containing a TI schema validated board config 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
+
+Output files:
+ - board config binary: File containing board configuration binary
+
+These above parameters are used only when the generated binary is
+intended to be a single board configuration binary. Example::
+
+ 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. The final binary is prepended with a header containing details about
+the included board config binaries. Example::
+
+ 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";
+ };
+ }
+ }
+
+
+
.. _etype_u_boot:
Entry: u-boot: U-Boot flat binary
diff --git a/tools/binman/etype/ti_board_config.py b/tools/binman/etype/ti_board_config.py
new file mode 100644
index 0000000..94f894c
--- /dev/null
+++ b/tools/binman/etype/ti_board_config.py
@@ -0,0 +1,259 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/
+# Written by Neha Malcom Francis <n-francis@ti.com>
+#
+# Entry-type module for generating schema validated TI board
+# configuration binary
+#
+
+import os
+import struct
+import yaml
+
+from collections import OrderedDict
+from jsonschema import validate
+from shutil import copyfileobj
+
+from binman.entry import Entry
+from binman.etype.section import Entry_section
+from dtoc import fdt_util
+from u_boot_pylib import tools
+
+BOARDCFG = 0xB
+BOARDCFG_SEC = 0xD
+BOARDCFG_PM = 0xE
+BOARDCFG_RM = 0xC
+BOARDCFG_NUM_ELEMS = 4
+
+class Entry_ti_board_config(Entry_section):
+ """An entry containing a TI schema validated board config 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
+
+ Output files:
+ - board config binary: File containing board configuration binary
+
+ These above parameters are used only when the generated binary is
+ intended to be a single board configuration binary. Example::
+
+ 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. The final binary is prepended with a header containing details about
+ the included board config binaries. Example::
+
+ 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 = None
+ self._schema = None
+ self._entries = 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 = fdt_util.GetString(self._node, 'config')
+ self._schema = fdt_util.GetString(self._node, 'schema')
+ # Depending on whether config file is present in node, we determine
+ # whether it is a combined board config binary or not
+ if self._config is None:
+ self.ReadEntries()
+ else:
+ self._config_file = tools.get_input_filename(self._config)
+ self._schema_file = tools.get_input_filename(self._schema)
+
+ def ReadEntries(self):
+ """Read the subnodes to find out what should go in this image
+ """
+ for node in self._node.subnodes:
+ if 'type' not in node.props:
+ entry = Entry.Create(self, node, 'ti-board-config')
+ entry.ReadNode()
+ cfg_data = entry.BuildSectionData(True)
+ entry._cfg_data = cfg_data
+ self._entries[entry.name] = entry
+ self._num_elems = len(self._node.subnodes)
+
+ def _convert_to_byte_chunk(self, val, data_type):
+ """Convert value into byte array
+
+ Args:
+ val: value to convert into byte array
+ data_type: data type used in schema, supported data types are u8,
+ u16 and u32
+
+ Returns:
+ array of bytes representing value
+ """
+ size = 0
+ if (data_type == '#/definitions/u8'):
+ size = 1
+ elif (data_type == '#/definitions/u16'):
+ size = 2
+ else:
+ size = 4
+ 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
+
+ Args:
+ schema_yaml: file containing YAML schema
+ file_yaml: file containing config to compile
+
+ Returns:
+ array of bytes repesenting YAML file against YAML schema
+ """
+ br = bytearray()
+ for key, node in file_yaml.items():
+ 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
+
+ Returns:
+ byte array containing config binary artifacts
+ or None if generation fails
+ """
+ cfg_binary = bytearray()
+ for key, node in self.file_yaml.items():
+ node_schema = self.schema_yaml['properties'][key]
+ br = self._compile_yaml(node_schema, node)
+ cfg_binary += br
+ return cfg_binary
+
+ def _add_boardcfg(self, bcfgtype, bcfgdata):
+ """Add board config to combined board config binary
+
+ Args:
+ bcfgtype (int): board config type
+ bcfgdata (byte array): board config data
+ """
+ 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):
+ """Generate final combined board config binary
+
+ Returns:
+ byte array containing combined board config data
+ or None if unable to generate
+ """
+ with open(self.descfile, 'rb') as desc_fh:
+ with open(self.bcfgfile, 'rb') as bcfg_fh:
+ with open(self.fh_file, 'ab+') as fh:
+ copyfileobj(desc_fh, fh)
+ copyfileobj(bcfg_fh, fh)
+ data = tools.read_file(self.fh_file)
+ return data
+
+ def BuildSectionData(self, required):
+ if self._config is None:
+ self._binary_offset = 0
+ uniq = self.GetUniqueName()
+ self.fh_file = tools.get_output_filename('fh.%s' % uniq)
+ self.descfile = tools.get_output_filename('desc.%s' % uniq)
+ self.bcfgfile = tools.get_output_filename('bcfg.%s' % uniq)
+
+ # when binman runs again make sure we start clean
+ if os.path.exists(self.fh_file):
+ os.remove(self.fh_file)
+ if os.path.exists(self.descfile):
+ os.remove(self.descfile)
+ if os.path.exists(self.bcfgfile):
+ os.remove(self.bcfgfile)
+
+ 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)
+
+ if 'board-cfg' in self._entries:
+ self._add_boardcfg(BOARDCFG, self._entries['board-cfg']._cfg_data)
+
+ if 'sec-cfg' in self._entries:
+ self._add_boardcfg(BOARDCFG_SEC, self._entries['sec-cfg']._cfg_data)
+
+ if 'pm-cfg' in self._entries:
+ self._add_boardcfg(BOARDCFG_PM, self._entries['pm-cfg']._cfg_data)
+
+ if 'rm-cfg' in self._entries:
+ self._add_boardcfg(BOARDCFG_RM, self._entries['rm-cfg']._cfg_data)
+
+ 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:
+ self.Raise(f"Schema validation error: {e}")
+
+ data = self._generate_binaries()
+ return data
+
+ def SetImagePos(self, image_pos):
+ Entry.SetImagePos(self, image_pos)
+
+ def CheckEntries(self):
+ Entry.CheckEntries(self)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index e53181a..5a3226e 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -97,6 +97,7 @@
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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# Subdirectory of the input dir to use to put test FDTs
TEST_FDT_SUBDIR = 'fdts'
@@ -199,6 +200,9 @@
shutil.copytree(cls.TestFile('files'),
os.path.join(cls._indir, 'files'))
+ shutil.copytree(cls.TestFile('yaml'),
+ os.path.join(cls._indir, 'yaml'))
+
TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
@@ -6884,6 +6888,22 @@
# Move to next
spl_data = content[:0x18]
+ 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 testTIBoardConfigCombined(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)
+
+ def testTIBoardConfigNoDataType(self):
+ """Test that error is thrown when data type is not supported"""
+ with self.assertRaises(ValueError) as e:
+ data = self._DoReadFile('279_ti_board_cfg_no_type.dts')
+ self.assertIn("Schema validation error", str(e.exception))
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 0000000..cda024c
--- /dev/null
+++ b/tools/binman/test/277_ti_board_cfg.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ ti-board-config {
+ config = "yaml/config.yaml";
+ schema = "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 0000000..95ef449
--- /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 = "yaml/config.yaml";
+ schema = "yaml/schema.yaml";
+ };
+ sec-cfg {
+ config = "yaml/config.yaml";
+ schema = "yaml/schema.yaml";
+ };
+ rm-cfg {
+ config = "yaml/config.yaml";
+ schema = "yaml/schema.yaml";
+ };
+ pm-cfg {
+ config = "yaml/config.yaml";
+ schema = "yaml/schema.yaml";
+ };
+ };
+ };
+};
diff --git a/tools/binman/test/279_ti_board_cfg_no_type.dts b/tools/binman/test/279_ti_board_cfg_no_type.dts
new file mode 100644
index 0000000..584b7ac
--- /dev/null
+++ b/tools/binman/test/279_ti_board_cfg_no_type.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+ binman {
+ ti-board-config {
+ config = "yaml/config.yaml";
+ schema = "yaml/schema_notype.yaml";
+ };
+ };
+};
diff --git a/tools/binman/test/yaml/config.yaml b/tools/binman/test/yaml/config.yaml
new file mode 100644
index 0000000..5f799a6
--- /dev/null
+++ b/tools/binman/test/yaml/config.yaml
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Test config
+#
+---
+
+main-branch:
+ obj:
+ a: 0x0
+ b: 0
+ arr: [0, 0, 0, 0]
+ another-arr:
+ - #1
+ c: 0
+ d: 0
+ - #2
+ c: 0
+ d: 0
diff --git a/tools/binman/test/yaml/schema.yaml b/tools/binman/test/yaml/schema.yaml
new file mode 100644
index 0000000..8aa03f3
--- /dev/null
+++ b/tools/binman/test/yaml/schema.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Test schema
+#
+---
+
+definitions:
+ u8:
+ type: integer
+ minimum: 0
+ maximum: 0xff
+ u16:
+ type: integer
+ minimum: 0
+ maximum: 0xffff
+ u32:
+ type: integer
+ minimum: 0
+ maximum: 0xffffffff
+
+type: object
+properties:
+ main-branch:
+ type: object
+ properties:
+ obj:
+ type: object
+ properties:
+ a:
+ $ref: "#/definitions/u32"
+ b:
+ $ref: "#/definitions/u16"
+ arr:
+ type: array
+ minItems: 4
+ maxItems: 4
+ items:
+ $ref: "#/definitions/u8"
+ another-arr:
+ type: array
+ minItems: 2
+ maxItems: 2
+ items:
+ type: object
+ properties:
+ c:
+ $ref: "#/definitions/u8"
+ d:
+ $ref: "#/definitions/u8"
diff --git a/tools/binman/test/yaml/schema_notype.yaml b/tools/binman/test/yaml/schema_notype.yaml
new file mode 100644
index 0000000..6b4d98f
--- /dev/null
+++ b/tools/binman/test/yaml/schema_notype.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Test schema
+#
+---
+
+definitions:
+ u8:
+ type: integer
+ minimum: 0
+ maximum: 0xff
+ u16:
+ type: integer
+ minimum: 0
+ maximum: 0xffff
+ u32:
+ type: integer
+ minimum: 0
+ maximum: 0xffffffff
+
+type: object
+properties:
+ main-branch:
+ type: object
+ properties:
+ obj:
+ type: object
+ properties:
+ a:
+ $ref: "#/definitions/u4"
+ b:
+ $ref: "#/definitions/u16"
+ arr:
+ type: array
+ minItems: 4
+ maxItems: 4
+ items:
+ $ref: "#/definitions/u8"