blob: 01dbb1ef5a63d1339a8fdaec986b1079ca4742b2 [file] [log] [blame]
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +03001"""
2Copyright 2023 Cypress Semiconductor Corporation (an Infineon company)
3or an affiliate of Cypress Semiconductor Corporation. All rights reserved.
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
17
18
19import sys
20import json
21import click
22
23def load_json(file_path):
24 """
25 Loads JSON from file.
26 """
27
28 data_json = None
29
30 try:
31 with open(file_path, encoding="utf-8") as file:
32 data_json = json.load(file)
33
34 except FileNotFoundError:
35 print(f'\nERROR: Cannot find {file_path}')
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020036 sys.exit(1)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030037
38 return data_json
39
40
41class FieldsValidator:
42 """
43 Validation of required fields and their cross-dependencies.
44 """
45
46 @staticmethod
47 def validate(feature_json, properties_json):
48 """
49 Check 'target' and properties of a platform.
50 """
51 p_target = properties_json.get('target')
52 if p_target is None:
53 raise AttributeError('Field "target" must be present in platform_properties.json')
54
55 f_target = feature_json.get('target')
56 if f_target is None:
57 raise AttributeError('Field "target" must be present in a feature_config.json')
58
59 if f_target not in p_target:
60 raise AttributeError('Target in feature config is not correct.'
61 ' It must be among the target list of platform_properties.json')
62
63 f_security_setup = feature_json.get('security_setup')
64 p_security_setup = properties_json.get('security_setup')
65
66 if f_security_setup:
67
68 if p_security_setup is None:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020069 print("\nThis platform doesn't have any 'secure_setup' features")
70 sys.exit(1)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030071
72 if f_security_setup.get('hw_rollback_prot'):
73 if p_security_setup.get('hw_rollback_prot') is None:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020074 print("\nThis platform doesn't have HW anti roll-back counter")
75 sys.exit(1)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030076
77 if f_security_setup.get('hw_crypto_acceleration'):
78 if p_security_setup.get('hw_crypto_acceleration') is None:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020079 print("\nThe platform doesn't support HW crypto acceleration")
80 sys.exit(1)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030081
82 if f_security_setup.get('validate_upgrade').get('value') is False:
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020083 if f_security_setup.get('validate_boot').get('value'):
84 print("\nERROR: Boot slot validation cannot be enabled if upgrade "\
85 "slot validation is disabled")
86 sys.exit(1)
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030087
88
89class FeatureProcessor:
90
91 """
92 The general handler of all needed fields and filling the new mk-file.
93 """
94
95 settings_dict = {
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020096 'validate_boot' : 'MCUBOOT_SKIP_BOOT_VALIDATION',
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030097 'validate_upgrade' : 'MCUBOOT_SKIP_UPGRADE_VALIDATION',
98 'dependency_check' : 'MCUBOOT_DEPENDENCY_CHECK',
99 'serial_logging' : 'MCUBOOT_LOG_LEVEL',
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200100 'watch_dog_timer' : 'USE_WDT',
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300101 'hw_rollback_prot' : 'USE_HW_ROLLBACK_PROT',
102 'hw_crypto_acceleration' : "USE_CRYPTO_HW",
103 'sw_downgrade_prev' : 'USE_SW_DOWNGRADE_PREV',
104 'ram_app_staging' : 'USE_STAGE_RAM_APPS',
105 'xip' : 'USE_XIP',
106 'image_encryption' : 'ENC_IMG',
107 'fault_injection_hardening' : 'FIH_PROFILE_LEVEL',
108 'combine_hex' : 'COMBINE_HEX',
109 'hw_key' : 'USE_HW_KEY'
110 }
111
112 debug_level_dict = {
113 'off' : '_OFF',
114 'error' : '_ERROR',
115 'warning' : '_WARNING',
116 'info' : '_INFO',
117 'debug' : '_DEBUG'
118 }
119
120 fih_level_dict = {
121 'off' : 'OFF',
122 'low' : 'LOW',
123 'medium' : 'MEDIUM',
124 'high' : 'HIGH'
125 }
126
127 def __init__(self, output_name):
128 self.out_f = output_name
129
130 @staticmethod
131 def generate_header_guard():
132 """
133 Print header line at the begining of a mk-file
134 """
135 guard_lines = ('# AUTO-GENERATED FILE, DO NOT EDIT.'
136 ' ALL CHANGES WILL BE LOST! #\n\n')
137
138 return guard_lines
139
140 @staticmethod
141 def insert_res(val_to_check) -> str:
142 """
143 Simlpe check result and return the string with value.
144 """
145 return f' := {1 if val_to_check else 0}\n'
146
147 @staticmethod
148 def insert_inverted_res(val_to_check) -> str:
149 """
150 Simlpe check result and return the string with inverted value.
151 """
152 return f' := {0 if val_to_check else 1}\n'
153
154 def __prnt_dict_primitive_key(self, dict_feature_config, settings_dict_key, f_out):
155 """
156 Print kyes of 'feature_config' with bool type of 'value'
157 """
158 val = dict_feature_config.get(settings_dict_key).get('value')
159
160 if isinstance(val, bool):
161
162 # invert because variable use 'skip' command
163 need_invertion = set(("validate_boot", "validate_upgrade"))
164
165 f_out.write(self.settings_dict[settings_dict_key])
166
167 if settings_dict_key not in need_invertion:
168 f_out.write(FeatureProcessor.insert_res(val))
169 else:
170 f_out.write(FeatureProcessor.insert_inverted_res(val))
171
172
173 def __gen_fih_level(self, fih_value):
174 """
175 Print only FIH_
176 """
177 res = f"{self.settings_dict['fault_injection_hardening']} ?= "\
178 f"{self.fih_level_dict[fih_value]}\n"
179
180 return res
181
182 def __gen_debug_level(self, logging_value):
183 """
184 Print only MCUBOOT_LOG_LEVEL
185 """
186 param_txt = self.settings_dict['serial_logging']
187 res_str = f"{param_txt} ?= {param_txt}{self.debug_level_dict[logging_value]}\n"
188
189 return res_str
190
191
192 def __handle_dictionary(self, f_dict, f_out):
193 """
194 Handle any dictionary of 'feature_config'
195 """
196 dont_print_list = set(("validation_key", "version", "description", "target"))
197
198 for k in f_dict:
199
200 if k not in dont_print_list:
201 self.__prnt_dict_primitive_key(f_dict, k, f_out)
202
203 if k == 'fault_injection_hardening':
204 f_out.write(self.__gen_fih_level(f_dict.get(k).get("value")))
205
206 if k == 'serial_logging':
207 f_out.write(self.__gen_debug_level(f_dict.get(k).get("value")))
208
209
210 def make_file_generate(self, feature_json):
211 """
212 Processing all keys and creation of a mk-file
213 """
214
215 with open(self.out_f, "w", encoding='UTF-8') as f_out:
216 f_out.write(FeatureProcessor.generate_header_guard())
217
218 f_security_setup_dict = feature_json.get('security_setup')
219
220 # handling of 'security_setup' section
221 if f_security_setup_dict:
222 self.__handle_dictionary(f_security_setup_dict, f_out)
223
224 self.__handle_dictionary(feature_json, f_out)
225
226
227@click.group()
228def cli():
229 """
230 Feature config parser to run from CLI
231 """
232
233@cli.command()
234@click.option('-f', '--feature_config', required=True,
235 help='feature configuration file path')
236@click.option('-p', '--platform_properties', required=True,
237 help='platform properties file path')
238@click.option('-n', '--output_name', required=True,
239 help='the name of the make file that will be generated')
240
241
242def run(feature_config, platform_properties, output_name):
243 """
244 The main CLI command to run mk-file generation
245 """
246
247 feature_config_json = load_json(feature_config)
248 platform_properties_json = load_json(platform_properties)
249
250 FieldsValidator.validate(feature_config_json, platform_properties_json)
251
252 fprocessor = FeatureProcessor(output_name)
253 fprocessor.make_file_generate(feature_config_json)
254
255
256if __name__ == '__main__':
257 cli()