-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpsa_builder.py
343 lines (294 loc) · 9.7 KB
/
psa_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#!/usr/bin/env python3
"""
Copyright (c) 2020-2021 ARM Limited. All rights reserved.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
import sys
import subprocess
import logging
import stat
try:
import yaml
except ImportError as e:
print(str(e) + " To install it, type:")
print("python3 -m pip install PyYAML")
exit(1)
dependencies = {
"released-tfm": {
"trusted-firmware-m": [
"https://git.trustedfirmware.org/TF-M/trusted-firmware-m.git",
"TF-Mv1.4.0",
],
},
"latest-tfm": {
"trusted-firmware-m": [
"https://git.trustedfirmware.org/TF-M/trusted-firmware-m.git",
"master",
],
},
"nuvoton-tfm": {
"trusted-firmware-m": [
"https://github.com/OpenNuvoton/trusted-firmware-m",
"nuvoton_mbed_m2354_tfm-1.4",
],
},
}
TC_DICT = {"ARMCLANG": "ARM", "GNUARM": "GCC_ARM"}
SUPPORTED_TFM_PSA_CONFIGS = ["PsaApiTestIPC"]
SUPPORTED_TFM_CONFIGS = [
"CoreIPC", # Default config
"RegressionIPC",
] + SUPPORTED_TFM_PSA_CONFIGS
PSA_SUITE_CHOICES = [
"CRYPTO",
"INITIAL_ATTESTATION",
"PROTECTED_STORAGE",
"INTERNAL_TRUSTED_STORAGE",
"STORAGE",
"IPC",
]
ROOT = os.path.abspath(os.path.dirname(__file__))
mbed_path = os.path.join(ROOT, "mbed-os")
TF_M_RELATIVE_PATH = "platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST"
sys.path.insert(0, mbed_path)
TF_M_BUILD_DIR = os.path.join(ROOT, "tfm", "repos")
POPEN_INSTANCE = None
from tools.targets import Target, TARGET_MAP, TARGET_NAMES
def are_dependencies_installed():
def _is_cmake_installed():
"""
Check if Cmake is installed
:return: errorcode
"""
command = ["cmake", "--version"]
return run_cmd_and_return(command)
def _is_git_installed():
"""
Check if git is installed
:return: errorcode
"""
command = ["git", "--version"]
return run_cmd_and_return(command)
def _is_srec_installed():
"""
Check if srec_cat is installed
:return: errorcode
"""
command = ["srec_cat", "--version"]
return run_cmd_and_return(command)
def _is_mbedgt_installed():
"""
Check if mbedgt is installed
:return: errorcode
"""
command = ["mbedgt", "--version"]
return run_cmd_and_return(command)
def _is_ninja_installed():
"""
Check if Ninja is installed
:return: errorcode
"""
command = ["ninja", "--version"]
return run_cmd_and_return(command)
if _is_git_installed() != 0:
logging.error('"git" is not installed. Exiting..')
return -1
elif _is_cmake_installed() != 0:
logging.error('"Cmake" is not installed. Exiting..')
return -1
elif _is_srec_installed() != 0:
logging.error('"srec_cat" is not installed. Exiting..')
return -1
elif _is_ninja_installed() != 0:
logging.error('"Ninja" is not installed. Exiting..')
return -1
elif _is_mbedgt_installed() != 0:
logging.error('"mbedgt" is not installed. Exiting..')
return -1
else:
return 0
def run_cmd_and_return(command, output=False):
"""
Run the command in the system and return either error code or output.
Commands are passed as a list of tokens.
E.g. The command 'git remote -v' would be passed in as:
['git', 'remote', '-v']
:param command: System command as a list of tokens
:param output: If set to True return output from child process
:return: Return either output from child process or error code
"""
global POPEN_INSTANCE
with open(os.devnull, "w") as fnull:
try:
POPEN_INSTANCE = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=fnull
)
except FileNotFoundError:
logging.error("Command not found: " + command[0])
return -1
std_out, __ = POPEN_INSTANCE.communicate()
retcode = POPEN_INSTANCE.returncode
POPEN_INSTANCE = None
if output:
return std_out.decode("utf-8")
else:
return retcode
def run_cmd_output_realtime(command, cmake_build_dir):
"""
Run the command in the system and print output in realtime.
Commands are passed as a list of tokens.
E.g. The command 'git remote -v' would be passed in as:
['git', 'remote', '-v']
:param command: System command as a list of tokens
:param cmake_build_dir: Cmake build directory
:return: Return the error code from child process
"""
global POPEN_INSTANCE
POPEN_INSTANCE = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=cmake_build_dir,
)
for line in iter(POPEN_INSTANCE.stdout.readline, b""):
logging.info(line.decode("utf-8").strip("\n"))
POPEN_INSTANCE.communicate()
retcode = POPEN_INSTANCE.returncode
POPEN_INSTANCE = None
return retcode
def check_and_clone_repo(name, deps, dir):
"""
Check if the repositories are already cloned. If not clone them
:param name: Name of the git repository
:param deps: Dictionary containing dependency details
:param dir: Directory to perform cloning
"""
gitref = dependencies[deps].get(name)[1]
tfm_dir = os.path.join(dir, name)
if not os.path.isdir(tfm_dir):
logging.info("Cloning %s repo", name)
cmd = [
"git",
"-C",
dir,
"clone",
"-o",
deps,
"-b",
gitref,
dependencies[deps].get(name)[0],
]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.critical("Failed to clone %s repo, error: %d", name, ret)
sys.exit(1)
logging.info("Cloned %s repo successfully", name)
else:
logging.info(
"%s repo exists, fetching latest from remote %s", name, deps
)
cmd = [
"git",
"-C",
tfm_dir,
"remote",
"get-url",
deps,
]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.info("%s is not a remote, adding it", deps)
cmd = [
"git",
"-C",
tfm_dir,
"remote",
"add",
deps,
dependencies[deps].get(name)[0],
]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.critical("Failed to add remote %s", deps)
sys.exit(1)
cmd = ["git", "-C", tfm_dir, "fetch", deps]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.critical(
"Failed to fetch the latest %s, error: %d", name, ret
)
sys.exit(1)
logging.info("Checking out %s..", gitref)
# try gitref as a remote branch
head = deps + "/" + gitref
cmd = ["git", "-C", tfm_dir, "checkout", "-B", gitref, head]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.info(
"%s is not a remote branch, trying %s directly", head, gitref
)
# gitref might be a tag or SHA1 which we checkout directly
cmd = ["git", "-C", tfm_dir, "checkout", gitref]
ret = run_cmd_and_return(cmd)
if ret != 0:
logging.critical(
"Failed to checkout %s, error: %d", gitref, ret
)
sys.exit(1)
logging.info("Checked out %s successfully", gitref)
def exit_gracefully(signum, frame):
"""
Crtl+C signal handler to exit gracefully
:param signum: Signal number
:param frame: Current stack frame object
"""
logging.info("Received signal %s, exiting.." % signum)
global POPEN_INSTANCE
try:
if POPEN_INSTANCE:
POPEN_INSTANCE.terminate()
while not POPEN_INSTANCE.poll():
continue
except:
pass
sys.exit(0)
def get_tfm_secure_targets():
"""
Creates a list of TF-M secure targets from Mbed OS targets.json.
:return: List of TF-M secure targets.
"""
return [str(t) for t in TARGET_NAMES if Target.get_target(t).is_TFM_target]
def get_tfm_regression_targets():
"""
Creates a list of TF-M regression tests supported targets
This parses the yaml file for supported target names and compares them
with TF-M targets supported in Mbed OS
:return: List of supported TF-M regression targets.
"""
with open(
os.path.join(os.path.dirname(__file__), "tfm_ns_import.yaml")
) as ns_import:
yaml_data = yaml.safe_load(ns_import)
mbed_os_data = yaml_data["mbed-os"]
regression_targets = list(
set(get_tfm_secure_targets()) & set(mbed_os_data)
)
return regression_targets
def handle_read_permission_error(func, path, exc_info):
"""
Handle read permission error when deleting a directory
It will try to change file permission and call the calling function again.
"""
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWUSR)
func(path)