--- /dev/null
+# 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)