Prepare v2023.10
[platform/kernel/u-boot.git] / tools / binman / etype / ti_board_config.py
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>
4 #
5 # Entry-type module for generating schema validated TI board
6 # configuration binary
7 #
8
9 import os
10 import struct
11 import yaml
12
13 from collections import OrderedDict
14 from jsonschema import validate
15 from shutil import copyfileobj
16
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
21
22 BOARDCFG = 0xB
23 BOARDCFG_SEC = 0xD
24 BOARDCFG_PM = 0xE
25 BOARDCFG_RM = 0xC
26 BOARDCFG_NUM_ELEMS = 4
27
28 class Entry_ti_board_config(Entry_section):
29     """An entry containing a TI schema validated board config binary
30
31     This etype supports generation of two kinds of board configuration
32     binaries: singular board config binary as well as combined board config
33     binary.
34
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
39
40     Output files:
41         - board config binary: File containing board configuration binary
42
43     These above parameters are used only when the generated binary is
44     intended to be a single board configuration binary. Example::
45
46         my-ti-board-config {
47             ti-board-config {
48                 config = "board-config.yaml";
49                 schema = "schema.yaml";
50             };
51         };
52
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::
58
59         my-combined-ti-board-config {
60             ti-board-config {
61                 board-cfg {
62                     config = "board-cfg.yaml";
63                     schema = "schema.yaml";
64                 };
65                 sec-cfg {
66                     config = "sec-cfg.yaml";
67                     schema = "schema.yaml";
68                 };
69             }
70         }
71     """
72     def __init__(self, section, etype, node):
73         super().__init__(section, etype, node)
74         self._config = None
75         self._schema = None
76         self._entries = OrderedDict()
77         self._num_elems = BOARDCFG_NUM_ELEMS
78         self._fmt = '<HHHBB'
79         self._index = 0
80         self._binary_offset = 0
81         self._sw_rev = 1
82         self._devgrp = 0
83
84     def ReadNode(self):
85         super().ReadNode()
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:
91             self.ReadEntries()
92         else:
93             self._config_file = tools.get_input_filename(self._config)
94             self._schema_file = tools.get_input_filename(self._schema)
95
96     def ReadEntries(self):
97         """Read the subnodes to find out what should go in this image
98         """
99         for node in self._node.subnodes:
100             if 'type' not in node.props:
101                 entry = Entry.Create(self, node, 'ti-board-config')
102                 entry.ReadNode()
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)
107
108     def _convert_to_byte_chunk(self, val, data_type):
109         """Convert value into byte array
110
111         Args:
112             val: value to convert into byte array
113             data_type: data type used in schema, supported data types are u8,
114                 u16 and u32
115
116         Returns:
117             array of bytes representing value
118         """
119         size = 0
120         if (data_type == '#/definitions/u8'):
121             size = 1
122         elif (data_type == '#/definitions/u16'):
123             size = 2
124         else:
125             size = 4
126         if type(val) == int:
127             br = val.to_bytes(size, byteorder='little')
128         return br
129
130     def _compile_yaml(self, schema_yaml, file_yaml):
131         """Convert YAML file into byte array based on YAML schema
132
133         Args:
134             schema_yaml: file containing YAML schema
135             file_yaml: file containing config to compile
136
137         Returns:
138             array of bytes repesenting YAML file against YAML schema
139         """
140         br = bytearray()
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':
150                 for item in node:
151                     if not isinstance(item, dict):
152                         br += self._convert_to_byte_chunk(
153                             item, schema_yaml['properties'][key]['items']['$ref'])
154                     else:
155                         br += self._compile_yaml(node_schema.get('items'), item)
156         return br
157
158     def _generate_binaries(self):
159         """Generate config binary artifacts from the loaded YAML configuration file
160
161         Returns:
162             byte array containing config binary artifacts
163             or None if generation fails
164         """
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)
169             cfg_binary += br
170         return cfg_binary
171
172     def _add_boardcfg(self, bcfgtype, bcfgdata):
173         """Add board config to combined board config binary
174
175         Args:
176             bcfgtype (int): board config type
177             bcfgdata (byte array): board config data
178         """
179         size = len(bcfgdata)
180         desc = struct.pack(self._fmt, bcfgtype,
181                             self._binary_offset, size, self._devgrp, 0)
182         with open(self.descfile, 'ab+') as desc_fh:
183             desc_fh.write(desc)
184         with open(self.bcfgfile, 'ab+') as bcfg_fh:
185             bcfg_fh.write(bcfgdata)
186         self._binary_offset += size
187         self._index += 1
188
189     def _finalize(self):
190         """Generate final combined board config binary
191
192         Returns:
193             byte array containing combined board config data
194             or None if unable to generate
195         """
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)
202         return data
203
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)
211
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)
219
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)
225
226             if 'board-cfg' in self._entries:
227                 self._add_boardcfg(BOARDCFG, self._entries['board-cfg']._cfg_data)
228
229             if 'sec-cfg' in self._entries:
230                 self._add_boardcfg(BOARDCFG_SEC, self._entries['sec-cfg']._cfg_data)
231
232             if 'pm-cfg' in self._entries:
233                 self._add_boardcfg(BOARDCFG_PM, self._entries['pm-cfg']._cfg_data)
234
235             if 'rm-cfg' in self._entries:
236                 self._add_boardcfg(BOARDCFG_RM, self._entries['rm-cfg']._cfg_data)
237
238             data = self._finalize()
239             return data
240
241         else:
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)
246
247             try:
248                 validate(self.file_yaml, self.schema_yaml)
249             except Exception as e:
250                 self.Raise(f"Schema validation error: {e}")
251
252             data = self._generate_binaries()
253             return data
254
255     def SetImagePos(self, image_pos):
256         Entry.SetImagePos(self, image_pos)
257
258     def CheckEntries(self):
259         Entry.CheckEntries(self)