[PATCH v5 01/23] binman: ti-board-config: Add support for TI board config binaries

Jan Kiszka jan.kiszka at siemens.com
Tue Jul 11 06:51:54 CEST 2023


On 07.07.23 14:34, Neha Malcom Francis wrote:
> 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.
> 
> Signed-off-by: Neha Malcom Francis <n-francis at ti.com>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> ---
>  tools/binman/entries.rst                      |  48 ++++
>  tools/binman/etype/ti_board_config.py         | 259 ++++++++++++++++++
>  tools/binman/ftest.py                         |  20 ++
>  tools/binman/test/277_ti_board_cfg.dts        |  14 +
>  .../binman/test/278_ti_board_cfg_combined.dts |  25 ++
>  .../binman/test/279_ti_board_cfg_no_type.dts  |  11 +
>  tools/binman/test/yaml/config.yaml            |  19 ++
>  tools/binman/test/yaml/schema.yaml            |  51 ++++
>  tools/binman/test/yaml/schema_notype.yaml     |  40 +++
>  9 files changed, 487 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/279_ti_board_cfg_no_type.dts
>  create mode 100644 tools/binman/test/yaml/config.yaml
>  create mode 100644 tools/binman/test/yaml/schema.yaml
>  create mode 100644 tools/binman/test/yaml/schema_notype.yaml
> 
> diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> index b71af801fd..14a2d03fad 100644
> --- a/tools/binman/entries.rst
> +++ b/tools/binman/entries.rst
> @@ -1658,6 +1658,54 @@ by setting the size of the entry to something larger than the text.
>  
>  
>  
> +.. _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 0000000000..0799e5dc59
> --- /dev/null
> +++ b/tools/binman/etype/ti_board_config.py
> @@ -0,0 +1,259 @@
> +# 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 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 43b4f850a6..b9a490a5bd 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\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 @@ class TestFunctional(unittest.TestCase):
>          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)
> @@ -6676,6 +6680,22 @@ 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 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 0000000000..cda024c1b8
> --- /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 0000000000..95ef449cbf
> --- /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 0000000000..584b7acc5a
> --- /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 0000000000..79fd67c7f4
> --- /dev/null
> +++ b/tools/binman/test/yaml/config.yaml
> @@ -0,0 +1,19 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
> +#
> +# 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 0000000000..60bf56f671
> --- /dev/null
> +++ b/tools/binman/test/yaml/schema.yaml
> @@ -0,0 +1,51 @@
> +# 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
> +    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 0000000000..d45d6cdf7e
> --- /dev/null
> +++ b/tools/binman/test/yaml/schema_notype.yaml
> @@ -0,0 +1,40 @@
> +# 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
> +    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"
> +

...
Applying: binman: ti-board-config: Add support for TI board config binaries
.git/rebase-apply/patch:538: new blank line at EOF.
+
.git/rebase-apply/patch:584: new blank line at EOF.
+
warning: 2 lines add whitespace errors.

Jan

-- 
Siemens AG, Technology
Competence Center Embedded Linux



More information about the U-Boot mailing list