Skip to content

TF-M: Python script to clone TF-M #11651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 11, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions tools/psa/build_tfm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/usr/bin/env python
"""
Copyright (c) 2019 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
from os.path import join, abspath, dirname, isdir
import sys
import shutil
import subprocess
import logging

logger = logging.getLogger('TF-M-Builder')
logging.basicConfig(level=logging.INFO,
format='[%(name)s] %(asctime)s: %(message)s.',
datefmt='%H:%M:%S')

ROOT = abspath(join(dirname(__file__), os.pardir, os.pardir))
sys.path.insert(0, ROOT)

TF_M_BUILD_DIR = join(ROOT, os.pardir, 'tfm_build_dir')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be good to use the mbed-os/BUILD directory to have everything there, as it is a dedicated build folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are planning to use the mbed-os importer to bring in TF-M PSA APIs for non-secure side. Therefore we cannot use mbed-os/BUILD directory. The importer script expects the source to be outside of mbed-os.
Another reason is not be interfere with mbed-cli building non-secure side (mbed-os). If we use mbed-os/BUILD then mbed-cli will try to compie tf-m sources when non-secure side is being built.

VERSION_FILE_PATH = join(ROOT, 'features/FEATURE_PSA/FEATURE_TFM')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message needs updating. It still says "components".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Patater fixed.


dependencies = {
"trusted-firmware-m":
['https://git.trustedfirmware.org/trusted-firmware-m.git',
'feature-twincpu'],
"mbedtls": ['https://github.com/ARMmbed/mbedtls.git',
'mbedtls-2.7.9'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's a really old Mbed TLS.

"mbed-crypto": ['https://github.com/ARMmbed/mbed-crypto.git',
'mbedcrypto-1.1.0'],
"CMSIS_5": ['https://github.com/ARM-software/CMSIS_5.git', '5.5.0'],
}

def is_git_installed():
"""
Check if git is installed
"""
command = ['git', '--version']
try:
with open(os.devnull, 'w') as fnull:
return subprocess.call(command, stdout=fnull, stderr=fnull)
except OSError as e:
return e.errno

def is_git_lfs_installed():
"""
Check if git-lfs is installed
"""
command = ['git-lfs', '--version']
try:
with open(os.devnull, 'w') as fnull:
return subprocess.call(command, stdout=fnull, stderr=fnull)
except OSError as e:
return e.errno

def run_cmd_and_return_output(command):
"""
Run the command in the sytem and return 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
"""
output = ''
logger.debug('[Exec] %s', ' '.join(command))
try:
with open(os.devnull, 'w') as fnull:
output = subprocess.check_output(command, stderr=fnull)
except subprocess.CalledProcessError as e:
logger.error("The command %s failed with return code: %s",
(' '.join(command)), e.returncode)
clean_up_cloned_repos()
return output.decode("utf-8")

def detect_and_write_tfm_version(tfm_dir):
"""
Identify the version of TF-M and write it to VERSION.txt
:param tfm_dir: The filesystem path where TF-M repo is cloned
"""
cmd = ['git', '-C', tfm_dir, 'describe', '--tags',
'--abbrev=12', '--dirty', '--always']
tfm_version = run_cmd_and_return_output(cmd)
logger.info('TF-M version: %s', tfm_version.strip('\n'))
if not isdir(VERSION_FILE_PATH):
os.makedirs(VERSION_FILE_PATH)
with open(join(VERSION_FILE_PATH, 'VERSION.txt'), 'w') as f:
f.write(tfm_version)

def check_repo_version(name, deps):
"""
Compare the version of cloned and expected and exit if they dont match
:param name: Name of the git repository
:param deps: Dictionary containing dependency details
"""
basedir = TF_M_BUILD_DIR
if name == 'trusted-firmware-m':
cmd = ['git', '-C', join(basedir, name),
'rev-parse', '--abbrev-ref', 'HEAD']
else:
cmd = ['git', '-C', join(basedir, name),
'describe', '--tags']
_out = run_cmd_and_return_output(cmd)
if _out.strip('\n') != deps.get(name)[1]:
Copy link
Contributor

@jainvikas8 jainvikas8 Oct 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting an error when executing this script
mbed-os$ /usr/bin/python3 /mbed-os/tools/psa/build_tfm.py [TF-M-Builder] 12:44:27: trusted-firmware-m repo exists, checking git version.... Traceback (most recent call last): File "/mbed-os/tools/psa/build_tfm.py", line 181, in <module> main() File "/mbed-os/tools/psa/build_tfm.py", line 173, in main clone_tfm_repo() File "/mbed-os/tools/psa/build_tfm.py", line 149, in clone_tfm_repo check_and_clone_repo('trusted-firmware-m', dependencies) File "/mbed-os/tools/psa/build_tfm.py", line 143, in check_and_clone_repo check_repo_version(name, deps) File "/mbed-os/tools/psa/build_tfm.py", line 119, in check_repo_version if _out.strip('\n') != deps.get(name)[1]: TypeError: a bytes-like object is required, not 'str'

Is it related to python3, if yes are we not supporting Python 3 as Python 2.7 support will be ending soon?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jainvikas8 Added support for python3

logger.error('Conflict: cloned "%s" and expected "%s"',
_out.strip('\n'), deps.get(name)[1])
logger.error('check and remove folder %s',
join(basedir, name))
sys.exit(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not call sys.exit(1). This will crash out of the program and will do no cleanup. It also will fail in the online system.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mark-edgeworth In this case we need to exit from the script without doing any cleanup since the cloned repos might contain user changes. Therefore, the only thing we do here is to warn the user and let them take the action.
Having said that, this path will never be triggered in the online system as there won't be any changes done by it.

else:
logger.info('%s: version check OK', name)

def check_and_clone_repo(name, deps):
"""
Test if the repositories are already cloned. If not clone them
:param name: Name of the git repository
:param deps: Dictionary containing dependency details
"""
basedir = TF_M_BUILD_DIR
if not isdir(join(basedir, name)):
logger.info('Cloning %s repo', name)
cmd = ['git', '-C', basedir, 'clone', '-b',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and other command calls to git) make assumptions that git is installed on the user's machine. None of this will work in the online build system either.

Copy link
Contributor Author

@urutva urutva Oct 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added is_git_installed() and is_git_lfs_installed() method to check the availability of required tools.

deps.get(name)[1], deps.get(name)[0]]
_out = run_cmd_and_return_output(cmd)
logger.info('Cloned %s repo successfully', name)
else:
logger.info('%s repo exists, checking git version...', name)
check_repo_version(name, deps)

def clone_tfm_repo():
"""
Clone TF-M git repos and it's dependencies
"""
check_and_clone_repo('trusted-firmware-m', dependencies)
check_and_clone_repo('mbedtls', dependencies)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this puts tf-m and all dependencies side by side into a single directly. How does TF-M pick up the dependencies and use them?

Copy link
Contributor Author

@urutva urutva Oct 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the preferred way of building TF-M. The dependencies are cloned outside the TF-M source tree and the cmake scripts handle the inclusion of dependent modules.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so we can point TF-M's build system to any filesystem location for the dependencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to check the cmake files but I think it is parent folder of tf-m is where the cmake will look for dependencies.

check_and_clone_repo('mbed-crypto', dependencies)
check_and_clone_repo('CMSIS_5', dependencies)
detect_and_write_tfm_version(join(TF_M_BUILD_DIR, 'trusted-firmware-m'))

def clean_up_cloned_repos():
"""
Clean up cloned repos in case of any errors
"""
try:
shutil.rmtree(TF_M_BUILD_DIR)
except OSError as e:
logger.error('Unable to cleanup cloned repos')
logger.error('"%s" occured', e.strerror)

def main():
"""
Build Trusted Firmware M (TF-M) image for mbed-os supported TF-M targets.
Current version of the script only clones TF-M git repo and dependencies
and creates a VERSION.txt file under 'features/FEATURE_PSA/FEATURE_TFM'
"""
if not isdir(TF_M_BUILD_DIR):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be ideal to delete all the files if the TF_M_BUILD_DIR is present. Because we are not checking which version of cloned repo is present when calling check_clone_repo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jainvikas8 It might be possible that users might have changes in the tf-m build directory. So it is not safe to assume that it can be deleted. Added a check to verify the version of cloned repos with the expected ones if the tf-m build directory already exists and warn the user if the versions don't match.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this would imply users can only have their changes saved locally, but can't pull in their git branches as the tag/version would change to a different HEAD. Is my understanding right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

os.mkdir(TF_M_BUILD_DIR)
clone_tfm_repo()

if __name__ == '__main__':
if is_git_installed() != 0:
logger.error('"git" is not installed. Exiting...')
elif is_git_lfs_installed() != 0:
logger.error('"git-lfs" is not installed. Exiting...')
else:
main()