1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/
3 # Written by Neha Malcom Francis <n-francis@ti.com>
5 # Entry-type module for generating schema validated TI board
13 from collections import OrderedDict
14 from jsonschema import validate
15 from shutil import copyfileobj
17 from binman.entry import Entry
18 from binman.etype.section import Entry_section
19 from dtoc import fdt_util
20 from u_boot_pylib import tools
26 BOARDCFG_NUM_ELEMS = 4
28 class Entry_ti_board_config(Entry_section):
29 """An entry containing a TI schema validated board config binary
31 This etype supports generation of two kinds of board configuration
32 binaries: singular board config binary as well as combined board config
35 Properties / Entry arguments:
36 - config-file: File containing board configuration data in YAML
37 - schema-file: File containing board configuration YAML schema against
38 which the config file is validated
41 - board config binary: File containing board configuration binary
43 These above parameters are used only when the generated binary is
44 intended to be a single board configuration binary. Example::
48 config = "board-config.yaml";
49 schema = "schema.yaml";
53 To generate a combined board configuration binary, we pack the
54 needed individual binaries into a ti-board-config binary. In this case,
55 the available supported subnode names are board-cfg, pm-cfg, sec-cfg and
56 rm-cfg. The final binary is prepended with a header containing details about
57 the included board config binaries. Example::
59 my-combined-ti-board-config {
62 config = "board-cfg.yaml";
63 schema = "schema.yaml";
66 config = "sec-cfg.yaml";
67 schema = "schema.yaml";
72 def __init__(self, section, etype, node):
73 super().__init__(section, etype, node)
76 self._entries = OrderedDict()
77 self._num_elems = BOARDCFG_NUM_ELEMS
80 self._binary_offset = 0
86 self._config = fdt_util.GetString(self._node, 'config')
87 self._schema = fdt_util.GetString(self._node, 'schema')
88 # Depending on whether config file is present in node, we determine
89 # whether it is a combined board config binary or not
90 if self._config is None:
93 self._config_file = tools.get_input_filename(self._config)
94 self._schema_file = tools.get_input_filename(self._schema)
96 def ReadEntries(self):
97 """Read the subnodes to find out what should go in this image
99 for node in self._node.subnodes:
100 if 'type' not in node.props:
101 entry = Entry.Create(self, node, 'ti-board-config')
103 cfg_data = entry.BuildSectionData(True)
104 entry._cfg_data = cfg_data
105 self._entries[entry.name] = entry
106 self._num_elems = len(self._node.subnodes)
108 def _convert_to_byte_chunk(self, val, data_type):
109 """Convert value into byte array
112 val: value to convert into byte array
113 data_type: data type used in schema, supported data types are u8,
117 array of bytes representing value
120 if (data_type == '#/definitions/u8'):
122 elif (data_type == '#/definitions/u16'):
127 br = val.to_bytes(size, byteorder='little')
130 def _compile_yaml(self, schema_yaml, file_yaml):
131 """Convert YAML file into byte array based on YAML schema
134 schema_yaml: file containing YAML schema
135 file_yaml: file containing config to compile
138 array of bytes repesenting YAML file against YAML schema
141 for key, node in file_yaml.items():
142 node_schema = schema_yaml['properties'][key]
143 node_type = node_schema.get('type')
144 if not 'type' in node_schema:
145 br += self._convert_to_byte_chunk(node,
146 node_schema.get('$ref'))
147 elif node_type == 'object':
148 br += self._compile_yaml(node_schema, node)
149 elif node_type == 'array':
151 if not isinstance(item, dict):
152 br += self._convert_to_byte_chunk(
153 item, schema_yaml['properties'][key]['items']['$ref'])
155 br += self._compile_yaml(node_schema.get('items'), item)
158 def _generate_binaries(self):
159 """Generate config binary artifacts from the loaded YAML configuration file
162 byte array containing config binary artifacts
163 or None if generation fails
165 cfg_binary = bytearray()
166 for key, node in self.file_yaml.items():
167 node_schema = self.schema_yaml['properties'][key]
168 br = self._compile_yaml(node_schema, node)
172 def _add_boardcfg(self, bcfgtype, bcfgdata):
173 """Add board config to combined board config binary
176 bcfgtype (int): board config type
177 bcfgdata (byte array): board config data
180 desc = struct.pack(self._fmt, bcfgtype,
181 self._binary_offset, size, self._devgrp, 0)
182 with open(self.descfile, 'ab+') as desc_fh:
184 with open(self.bcfgfile, 'ab+') as bcfg_fh:
185 bcfg_fh.write(bcfgdata)
186 self._binary_offset += size
190 """Generate final combined board config binary
193 byte array containing combined board config data
194 or None if unable to generate
196 with open(self.descfile, 'rb') as desc_fh:
197 with open(self.bcfgfile, 'rb') as bcfg_fh:
198 with open(self.fh_file, 'ab+') as fh:
199 copyfileobj(desc_fh, fh)
200 copyfileobj(bcfg_fh, fh)
201 data = tools.read_file(self.fh_file)
204 def BuildSectionData(self, required):
205 if self._config is None:
206 self._binary_offset = 0
207 uniq = self.GetUniqueName()
208 self.fh_file = tools.get_output_filename('fh.%s' % uniq)
209 self.descfile = tools.get_output_filename('desc.%s' % uniq)
210 self.bcfgfile = tools.get_output_filename('bcfg.%s' % uniq)
212 # when binman runs again make sure we start clean
213 if os.path.exists(self.fh_file):
214 os.remove(self.fh_file)
215 if os.path.exists(self.descfile):
216 os.remove(self.descfile)
217 if os.path.exists(self.bcfgfile):
218 os.remove(self.bcfgfile)
220 with open(self.fh_file, 'wb') as f:
221 t_bytes = f.write(struct.pack(
222 '<BB', self._num_elems, self._sw_rev))
223 self._binary_offset += t_bytes
224 self._binary_offset += self._num_elems * struct.calcsize(self._fmt)
226 if 'board-cfg' in self._entries:
227 self._add_boardcfg(BOARDCFG, self._entries['board-cfg']._cfg_data)
229 if 'sec-cfg' in self._entries:
230 self._add_boardcfg(BOARDCFG_SEC, self._entries['sec-cfg']._cfg_data)
232 if 'pm-cfg' in self._entries:
233 self._add_boardcfg(BOARDCFG_PM, self._entries['pm-cfg']._cfg_data)
235 if 'rm-cfg' in self._entries:
236 self._add_boardcfg(BOARDCFG_RM, self._entries['rm-cfg']._cfg_data)
238 data = self._finalize()
242 with open(self._config_file, 'r') as f:
243 self.file_yaml = yaml.safe_load(f)
244 with open(self._schema_file, 'r') as sch:
245 self.schema_yaml = yaml.safe_load(sch)
248 validate(self.file_yaml, self.schema_yaml)
249 except Exception as e:
250 self.Raise(f"Schema validation error: {e}")
252 data = self._generate_binaries()
255 def SetImagePos(self, image_pos):
256 Entry.SetImagePos(self, image_pos)
258 def CheckEntries(self):
259 Entry.CheckEntries(self)