[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