mirror of https://github.com/ARMmbed/mbed-os.git
Cleanup tools folder and update travis file
1. Remove psa related hooks from the build system. 2. Remove PSA related scripts from tools/test folder 3. Remove psa-autogen job from travis which was running generate_partition_code.py and is not needed anymore 4. Install python modules needed for events and littlefs tests Signed-off-by: Devaraj Ranganna <devaraj.ranganna@arm.com>pull/12402/head
parent
4b070bf2b5
commit
d8d7ad0217
11
.travis.yml
11
.travis.yml
|
@ -195,8 +195,8 @@ matrix:
|
|||
### Extended Tests ###
|
||||
- &extended-vm
|
||||
stage: "Extended"
|
||||
name: "psa autogen"
|
||||
env: NAME=psa-autogen
|
||||
name: "events"
|
||||
env: NAME=events EVENTS=events
|
||||
language: python
|
||||
python: 3.7
|
||||
install:
|
||||
|
@ -208,13 +208,6 @@ matrix:
|
|||
- python -m pip install --upgrade setuptools==40.4.3
|
||||
- pip install -r requirements.txt
|
||||
- pip list --verbose
|
||||
script:
|
||||
- python tools/psa/generate_partition_code.py
|
||||
- git diff --exit-code
|
||||
|
||||
- <<: *extended-vm
|
||||
name: "events"
|
||||
env: NAME=events EVENTS=events
|
||||
script:
|
||||
# Check that example compiles
|
||||
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' ${EVENTS}/README.md > main.cpp
|
||||
|
|
|
@ -44,8 +44,6 @@ from tools.utils import argparse_filestring_type, args_error, argparse_many
|
|||
from tools.utils import argparse_dir_not_parent
|
||||
from tools.utils import NoValidToolchainException
|
||||
from tools.utils import print_end_warnings
|
||||
from tools.psa import generate_psa_sources
|
||||
from tools.resources import OsAndSpeResourceFilter
|
||||
|
||||
def main():
|
||||
start = time()
|
||||
|
@ -189,12 +187,6 @@ def main():
|
|||
|
||||
if options.source_dir:
|
||||
resource_filter = None
|
||||
if target.is_PSA_secure_target:
|
||||
generate_psa_sources(
|
||||
source_dirs=options.source_dir,
|
||||
ignore_paths=[options.build_dir]
|
||||
)
|
||||
resource_filter = OsAndSpeResourceFilter()
|
||||
|
||||
lib_build_res = build_library(
|
||||
options.source_dir, options.build_dir, target, toolchain_name,
|
||||
|
|
|
@ -41,7 +41,7 @@ from .paths import (MBED_CMSIS_PATH, MBED_TARGETS_PATH, MBED_LIBRARIES,
|
|||
MBED_CONFIG_FILE, MBED_LIBRARIES_DRIVERS,
|
||||
MBED_LIBRARIES_PLATFORM, MBED_LIBRARIES_HAL,
|
||||
BUILD_DIR)
|
||||
from .resources import Resources, FileType, FileRef, PsaManifestResourceFilter
|
||||
from .resources import Resources, FileType, FileRef
|
||||
from .notifier.mock import MockNotifier
|
||||
from .targets import TARGET_NAMES, TARGET_MAP, CORE_ARCH, Target
|
||||
from .libraries import Library
|
||||
|
@ -753,7 +753,6 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
|||
res = Resources(notify).scan_with_toolchain(
|
||||
src_paths, toolchain, dependencies_paths, inc_dirs=inc_dirs)
|
||||
res.filter(resource_filter)
|
||||
res.filter(PsaManifestResourceFilter())
|
||||
|
||||
# Copy headers, objects and static libraries - all files needed for
|
||||
# static lib
|
||||
|
|
|
@ -55,8 +55,6 @@ from tools.utils import print_end_warnings
|
|||
from tools.utils import print_large_string
|
||||
from tools.settings import ROOT
|
||||
from tools.targets import Target
|
||||
from tools.psa import generate_psa_sources
|
||||
from tools.resources import OsAndSpeResourceFilter
|
||||
|
||||
def default_args_dict(options):
|
||||
return dict(
|
||||
|
@ -336,13 +334,6 @@ def main():
|
|||
|
||||
if options.source_dir is not None:
|
||||
resource_filter = None
|
||||
if target.is_PSA_secure_target:
|
||||
generate_psa_sources(
|
||||
source_dirs=options.source_dir,
|
||||
ignore_paths=[options.build_dir]
|
||||
)
|
||||
resource_filter = OsAndSpeResourceFilter()
|
||||
|
||||
wrapped_build_project(
|
||||
options.source_dir,
|
||||
options.build_dir,
|
||||
|
|
|
@ -53,8 +53,6 @@ from tools.utils import print_large_string
|
|||
from tools.utils import NotSupportedException
|
||||
from tools.options import extract_profile, list_profiles, extract_mcus
|
||||
from tools.notifier.term import TerminalNotifier
|
||||
from tools.psa import generate_psa_sources
|
||||
from tools.resources import OsAndSpeResourceFilter
|
||||
|
||||
""" The CLI entry point for exporting projects from the mbed tools to any of the
|
||||
supported IDEs or project structures.
|
||||
|
@ -389,14 +387,7 @@ def main():
|
|||
args_error(parser, "%s not supported by %s" % (mcu, ide))
|
||||
|
||||
try:
|
||||
target = Target.get_target(mcu)
|
||||
resource_filter = None
|
||||
if target.is_PSA_secure_target:
|
||||
generate_psa_sources(source_dirs=options.source_dir,
|
||||
ignore_paths=[]
|
||||
)
|
||||
resource_filter = OsAndSpeResourceFilter()
|
||||
|
||||
export(
|
||||
mcu,
|
||||
ide,
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
# PSA tools
|
||||
|
||||
## Code generation script
|
||||
|
||||
Mbed-OS contains two implementations of PSA Firmware Framework:
|
||||
|
||||
* Mbed-SPM - Implementation for dual-core v7 targets.
|
||||
* TF-M - Implementation for v8 targets.
|
||||
|
||||
Both PSA Firmware Framework implementation impose the following requirements:
|
||||
|
||||
* PSA manifests must be valid according to the JSON schema file provided by PSA FF spec.
|
||||
* There are no conflicts between various PSA manifests (duplicate SIDs and PIDs, dependencies, etc.)
|
||||
* Secure partition initialization code to be present at mbed-os core compile time.
|
||||
|
||||
To satisfy the requirement listed above, Mbed-OS build system invokes `generate_partition_code.py` script
|
||||
during the build process for PSA targets.
|
||||
|
||||
PSA code generation step has the following effects:
|
||||
* Scan the whole source tree for PSA manifest files, including application (in case invoked from application directory) and all the `TESTS` directories.
|
||||
* All found PSA manifest files get parsed and validated.
|
||||
* Source and header files for initializing SPM are generated. Test related partitions and SIDs are disabled by default by `#ifndef` guards.
|
||||
To enable them following defines must be passed to build command (typically done automatically via [release.py](#secure-image-generation)):
|
||||
* `-DUSE_PSA_TEST_PARTITIONS`
|
||||
* `-DUSE_<name>` where `<name>` corresponds to the name in PSA manifest file (`"name"` property).
|
||||
|
||||
## Secure image generation
|
||||
|
||||
`release.py` is the script assigned with compiling the default secure images.
|
||||
|
||||
For an application with custom secure portions, the secure image should be generated by invoking `mbed-cli` directly.
|
||||
|
||||
> **Note**: when building targets utilizing TF-M PSA implementations, add the following arguments to a build command for the secure image:
|
||||
`--app-config <mbed-os-root>/tools/psa/tfm/mbed_app.json`
|
||||
|
||||
### Usage
|
||||
```text
|
||||
usage: release.py [-h] [-m MCU] [-t TC] [-d] [-q] [-l] [--commit]
|
||||
[--skip-tests] [-x ...]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-m MCU, --mcu MCU build for the given MCU
|
||||
-t TC, --tc TC build for the given tool chain (default is
|
||||
default_toolchain)
|
||||
-d, --debug set build profile to debug
|
||||
-q, --quiet No Build log will be printed
|
||||
-l, --list Print supported PSA secure targets
|
||||
--commit create a git commit for each platform
|
||||
--skip-tests skip the test build phase
|
||||
-x ..., --extra ... additional build parameters
|
||||
```
|
||||
|
||||
* The script must be run from the mbed-os folder via `tools/psa/release.py`,
|
||||
otherwise the list of available tests will not be accurate and the test
|
||||
partitions will not be properly generated.
|
||||
* When `MCU ` is not specified, the script compiles all the images for all the targets.
|
||||
* When `-t/--tc` is not specified, the script compiles with the default_toolchain speciified in targets.json.
|
||||
* When `-d/--debug` is not specified, the script compiles the images using the release profile.
|
||||
* When `--commit` is not specified, the script will not commit the images to git and
|
||||
any auto-generated PSA related components and services.
|
||||
* A user can specify additional commands that will be passed on to the build commands (Ex. -D for compilation defines).
|
||||
|
||||
This script should be run in following scenarios:
|
||||
|
||||
* Release.
|
||||
* Update to files originating in the secure side.
|
||||
* Drivers update.
|
||||
* PSA updates.
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2019 ARM Limited
|
||||
#
|
||||
# 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 shutil
|
||||
|
||||
from tools.resources import FileType
|
||||
from tools.settings import ROOT
|
||||
from .generate_partition_code import manifests_discovery, generate_spm_code
|
||||
|
||||
|
||||
|
||||
def find_secure_image(notify, resources, ns_image_path,
|
||||
configured_s_image_filename, image_type):
|
||||
""" Find secure image. """
|
||||
if configured_s_image_filename is None:
|
||||
return None
|
||||
|
||||
assert ns_image_path and configured_s_image_filename, \
|
||||
'ns_image_path and configured_s_image_path are mandatory'
|
||||
assert image_type in [FileType.BIN, FileType.HEX], \
|
||||
'image_type must be of type BIN or HEX'
|
||||
|
||||
image_files = resources.get_file_paths(image_type)
|
||||
assert image_files, 'No image files found for this target'
|
||||
|
||||
secure_image = next(
|
||||
(f for f in image_files if
|
||||
os.path.basename(f) == configured_s_image_filename), None)
|
||||
secure_image = next(
|
||||
(f for f in image_files if
|
||||
os.path.splitext(os.path.basename(f))[0] ==
|
||||
os.path.splitext(os.path.basename(ns_image_path))[0]),
|
||||
secure_image
|
||||
)
|
||||
|
||||
if secure_image:
|
||||
notify.debug("Secure image file found: %s." % secure_image)
|
||||
else:
|
||||
notify.debug("Secure image file %s not found. Aborting."
|
||||
% configured_s_image_filename)
|
||||
raise Exception("Required secure image not found.")
|
||||
|
||||
return secure_image
|
||||
|
||||
def generate_psa_sources(source_dirs, ignore_paths):
|
||||
services, apps = manifests_discovery(root_dirs=source_dirs,
|
||||
ignore_paths=ignore_paths + ['.git'])
|
||||
assert len(services + apps), 'PSA manifest discovery failed'
|
||||
psa_out_dir = os.path.join(ROOT, 'components', 'TARGET_PSA')
|
||||
|
||||
generate_spm_code(services, apps, psa_out_dir)
|
||||
return psa_out_dir
|
|
@ -1,129 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2017-2019 ARM Limited
|
||||
#
|
||||
# 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 argparse
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from os.path import join as path_join
|
||||
|
||||
# Be sure that the tools directory is in the search path
|
||||
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
from tools.psa.mbed_spm_tfm_common import validate_partition_manifests, \
|
||||
manifests_discovery, parse_manifests, generate_source_files, \
|
||||
MBED_OS_ROOT
|
||||
|
||||
__version__ = '1.1'
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
MANIFEST_FILE_PATTERN = '*_psa.json'
|
||||
PSA_CORE_ROOT = path_join(MBED_OS_ROOT, 'components', 'TARGET_PSA')
|
||||
TEMPLATES_DESC = path_join(SCRIPT_DIR, 'spm_template_file_list.json')
|
||||
|
||||
|
||||
def _get_timestamp(f):
|
||||
return os.path.getmtime(f) if os.path.isfile(f) else 0
|
||||
|
||||
|
||||
def is_up_to_date(manifest_files, out_files):
|
||||
manifest_timestamp = max(_get_timestamp(f) for f in manifest_files)
|
||||
out_timestamps = min(_get_timestamp(f) for f in out_files)
|
||||
return manifest_timestamp <= out_timestamps
|
||||
|
||||
|
||||
def generate_spm_code(service_files, app_files, output_dir):
|
||||
with open(TEMPLATES_DESC, 'r') as fh:
|
||||
templates_data = json.load(fh)
|
||||
templates_dict = {
|
||||
path_join(MBED_OS_ROOT, t['template']):
|
||||
path_join(output_dir, t['output']) for t in templates_data
|
||||
}
|
||||
|
||||
if is_up_to_date(service_files + app_files, list(templates_dict.values())):
|
||||
return
|
||||
|
||||
# Construct lists of all the manifests and mmio_regions.
|
||||
service_manifests, service_region_list = parse_manifests(service_files)
|
||||
test_manifests, test_region_list = parse_manifests(app_files)
|
||||
|
||||
# Validate the correctness of the manifest collection.
|
||||
validate_partition_manifests(service_manifests + test_manifests)
|
||||
|
||||
region_list = service_region_list + test_region_list
|
||||
|
||||
render_args = {
|
||||
'service_partitions': service_manifests,
|
||||
'test_partitions': test_manifests,
|
||||
'script_ver': __version__,
|
||||
'regions': region_list,
|
||||
'region_pair_list': list(itertools.combinations(region_list, 2)),
|
||||
}
|
||||
|
||||
generate_source_files(templates_dict, render_args)
|
||||
|
||||
|
||||
class AppendReadableDir(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
prosp_dir = os.path.abspath(values)
|
||||
if not os.path.isdir(prosp_dir):
|
||||
raise argparse.ArgumentTypeError("{} is missing".format(prosp_dir))
|
||||
if not os.access(prosp_dir, os.R_OK):
|
||||
raise argparse.ArgumentTypeError(
|
||||
"{} is not a accessible for read".format(prosp_dir))
|
||||
if not getattr(namespace, self.dest):
|
||||
setattr(namespace, self.dest, [])
|
||||
getattr(namespace, self.dest).append(prosp_dir)
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='PSA SPM code generator',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
'-u', '--user-app',
|
||||
action=AppendReadableDir,
|
||||
default=[ROOT],
|
||||
help='Root directory for recursive PSA manifest scan. Use for adding '
|
||||
'application specific secure partitions. Can be supplied more '
|
||||
'than once',
|
||||
metavar='DIR'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-o', '--output-dir',
|
||||
default=ROOT,
|
||||
help='Root directory for generating the sources',
|
||||
metavar='DIR'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
services, apps = manifests_discovery(root_dirs=args.user_app,
|
||||
ignore_paths=['BUILD', '.git'])
|
||||
|
||||
generate_spm_code(services, apps, args.output_dir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,678 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2017-2018 ARM Limited
|
||||
#
|
||||
# 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 as path_join
|
||||
import json
|
||||
from jsonschema import validate
|
||||
import fnmatch
|
||||
from six import integer_types, string_types
|
||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir))
|
||||
SERVICES_DIR = path_join(MBED_OS_ROOT, "components", "TARGET_PSA", "services")
|
||||
TESTS_DIR = path_join(MBED_OS_ROOT, "TESTS", "psa")
|
||||
MANIFEST_FILE_PATTERN = '*_psa.json'
|
||||
|
||||
|
||||
def _assert_int(num):
|
||||
"""
|
||||
Tries to parse an integer num from a given string
|
||||
|
||||
:param num: Number in int/string type
|
||||
:return: Numeric value
|
||||
"""
|
||||
if isinstance(num, int):
|
||||
return num
|
||||
num_str = str(num)
|
||||
radix = 16 if num_str.lower().startswith('0x') else 10
|
||||
res = int(num_str, radix)
|
||||
# Python converts str to int as a signed integer
|
||||
if res > 0x7FFFFFFF:
|
||||
res -= 0x100000000
|
||||
return res
|
||||
|
||||
|
||||
class RotService(object):
|
||||
MINOR_POLICIES = ['STRICT', 'RELAXED']
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
identifier,
|
||||
signal,
|
||||
non_secure_clients,
|
||||
minor_version=1,
|
||||
minor_policy='STRICT'
|
||||
):
|
||||
"""
|
||||
Root of Trust Service C'tor (Aligned with json schema)
|
||||
|
||||
:param name: Root of Trust Service identifier (available to user)
|
||||
:param identifier: Root of Trust Service numeric enumeration.
|
||||
:param signal: Root of Trust Service identifier inside the partition
|
||||
:param non_secure_clients: True to allow connections from non-secure
|
||||
partitions
|
||||
:param minor_version: Root of Trust Service version
|
||||
:param minor_policy: Enforcement level of minor version
|
||||
"""
|
||||
self.name = name
|
||||
self.id = identifier
|
||||
self.signal = signal
|
||||
|
||||
assert _assert_int(identifier)
|
||||
|
||||
assert isinstance(non_secure_clients, bool), \
|
||||
'non_secure_clients parameter must be of boolean type'
|
||||
self.nspe_callable = non_secure_clients
|
||||
|
||||
self.minor_version = _assert_int(minor_version)
|
||||
assert self.minor_version > 0, 'minor_version parameter is invalid'
|
||||
|
||||
assert minor_policy in self.MINOR_POLICIES, \
|
||||
'minor_policy parameter is invalid'
|
||||
self.minor_policy = minor_policy
|
||||
|
||||
@property
|
||||
def numeric_id(self):
|
||||
return _assert_int(self.id)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
(self.name == other.name) and
|
||||
(self.id == other.id) and
|
||||
(self.signal == other.signal) and
|
||||
(self.nspe_callable == other.nspe_callable) and
|
||||
(self.minor_version == other.minor_version) and
|
||||
(self.minor_policy == other.minor_policy)
|
||||
)
|
||||
|
||||
|
||||
class MmioRegion(object):
|
||||
MMIO_PERMISSIONS = {
|
||||
'READ-ONLY': 'PSA_MMIO_PERM_READ_ONLY',
|
||||
'READ-WRITE': 'PSA_MMIO_PERM_READ_WRITE'
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
MMIO Region C'tor (Aligned with json schema)
|
||||
|
||||
Supports both named and numeric regions
|
||||
In case of named region the acceptable params are name and permission
|
||||
In case of numeric region the acceptable params are name, size and
|
||||
permission
|
||||
|
||||
:param name: C definition name of the region (size will be
|
||||
auto-generated)
|
||||
:param base: C hex string defining a memory address (must be 32bit)
|
||||
:param size: size of a region (Applicable only for numbered regions)
|
||||
:param permission: Access permissions to the described region (R/RW)
|
||||
|
||||
"""
|
||||
assert 'permission' in kwargs
|
||||
self.permission = self.MMIO_PERMISSIONS[kwargs['permission']]
|
||||
if 'name' in kwargs:
|
||||
self.base = kwargs['name']
|
||||
self.size = '(sizeof(*({})))'.format(kwargs['name'])
|
||||
if 'base' in kwargs:
|
||||
self.base = kwargs['base']
|
||||
self.size = _assert_int(kwargs['size'])
|
||||
|
||||
assert 'partition_id' in kwargs
|
||||
self.partition_id = _assert_int(kwargs['partition_id'])
|
||||
|
||||
assert hasattr(self, 'base')
|
||||
assert hasattr(self, 'size')
|
||||
assert hasattr(self, 'permission')
|
||||
assert hasattr(self, 'partition_id')
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
(self.base == other.base) and
|
||||
(self.size == other.size) and
|
||||
(self.permission == other.permission)
|
||||
)
|
||||
|
||||
|
||||
class Irq(object):
|
||||
def __init__(self, line_num, signal):
|
||||
"""
|
||||
IRQ line C'tor (Aligned with json schema)
|
||||
|
||||
:param line_num: number of interrupt used by the partition
|
||||
:param signal: IRQ line identifier inside the partition
|
||||
"""
|
||||
self.line_num = _assert_int(line_num)
|
||||
assert isinstance(signal, string_types)
|
||||
self.signal = signal
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.line_num == other.line_num) and \
|
||||
(self.signal == other.signal)
|
||||
|
||||
|
||||
class Manifest(object):
|
||||
PRIORITY = {
|
||||
'LOW': 'osPriorityLow',
|
||||
'NORMAL': 'osPriorityNormal',
|
||||
'HIGH': 'osPriorityHigh'
|
||||
}
|
||||
PARTITION_TYPES = ['APPLICATION-ROT', 'PSA-ROT']
|
||||
# The following signal bits cannot be used:
|
||||
# bit[0-2] | Reserved
|
||||
# bit[3] | PSA Doorbell
|
||||
# bit[31] | RTX error bit
|
||||
RESERVED_SIGNALS = 5
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
manifest_file,
|
||||
name,
|
||||
partition_id,
|
||||
partition_type,
|
||||
priority,
|
||||
entry_point,
|
||||
heap_size,
|
||||
stack_size,
|
||||
source_files,
|
||||
mmio_regions=None,
|
||||
rot_services=None,
|
||||
extern_sids=None,
|
||||
irqs=None
|
||||
):
|
||||
"""
|
||||
Manifest C'tor (Aligned with json schema)
|
||||
|
||||
:param manifest_file: Path to json manifest
|
||||
:param name: Partition unique name
|
||||
:param partition_id: Partition identifier
|
||||
:param partition_type: Whether the partition is unprivileged or part
|
||||
of the trusted computing base
|
||||
:param priority: Priority of the partition's thread
|
||||
:param entry_point: C symbol name of the partition's main function
|
||||
:param heap_size: Size of heap required for the partition
|
||||
:param stack_size: Size of stack required for the partition
|
||||
:param source_files: List of files assembling the partition
|
||||
(relative paths)
|
||||
:param mmio_regions: List of MMIO regions used by the partition
|
||||
:param rot_services: List of Root of Trust Services declared by the
|
||||
partition
|
||||
:param extern_sids: List of Root of Trust Services the partition can call
|
||||
:param irqs: List of interrupts the partition can handle
|
||||
"""
|
||||
assert manifest_file is not None
|
||||
assert name is not None
|
||||
assert partition_id is not None
|
||||
assert partition_type is not None
|
||||
assert entry_point is not None
|
||||
assert priority is not None
|
||||
assert heap_size is not None
|
||||
assert stack_size is not None
|
||||
assert source_files is not None
|
||||
|
||||
mmio_regions = [] if mmio_regions is None else mmio_regions
|
||||
rot_services = [] if rot_services is None else rot_services
|
||||
extern_sids = [] if extern_sids is None else extern_sids
|
||||
irqs = [] if irqs is None else irqs
|
||||
|
||||
assert os.path.isfile(manifest_file)
|
||||
assert isinstance(partition_id, integer_types)
|
||||
assert isinstance(heap_size, int)
|
||||
assert isinstance(stack_size, int)
|
||||
assert isinstance(entry_point, string_types)
|
||||
assert partition_type in self.PARTITION_TYPES
|
||||
assert partition_id > 0
|
||||
|
||||
self.file = manifest_file
|
||||
self.name = name
|
||||
self.id = partition_id
|
||||
self.type = partition_type
|
||||
|
||||
self.priority_tfm = priority
|
||||
self.priority_mbed = self.PRIORITY[priority]
|
||||
self.heap_size = heap_size
|
||||
self.stack_size = stack_size
|
||||
self.entry_point = entry_point
|
||||
if isinstance(source_files, list):
|
||||
self.source_files = source_files
|
||||
else:
|
||||
self.source_files = [source_files]
|
||||
|
||||
self.mmio_regions = mmio_regions
|
||||
self.rot_services = rot_services
|
||||
self.extern_sids = extern_sids
|
||||
self.irqs = irqs
|
||||
|
||||
for src_file in self.source_files:
|
||||
assert os.path.isfile(src_file), \
|
||||
"The source file {} mentioned in {} doesn't exist.".format(
|
||||
src_file, self.file
|
||||
)
|
||||
|
||||
for rot_srv in self.rot_services:
|
||||
assert isinstance(rot_srv, RotService)
|
||||
|
||||
for extern_sid in self.extern_sids:
|
||||
assert isinstance(extern_sid, string_types)
|
||||
|
||||
assert len(self.extern_sids) == len(set(self.extern_sids)), \
|
||||
'Detected duplicates external SIDs in {}'.format(self.file)
|
||||
|
||||
for irq in self.irqs:
|
||||
assert isinstance(irq, Irq)
|
||||
|
||||
total_signals = len(self.rot_services) + len(self.irqs)
|
||||
assert total_signals <= 32 - self.RESERVED_SIGNALS, \
|
||||
'Manifest {} - {} exceeds limit of RoT services and IRQs allowed ' \
|
||||
'({}).'.format(
|
||||
self.name, self.file, 32 - self.RESERVED_SIGNALS
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
(self.file == other.file) and
|
||||
(self.name == other.name) and
|
||||
(self.id == other.id) and
|
||||
(self.type == other.type) and
|
||||
(self.priority_mbed == other.priority_mbed) and
|
||||
(self.priority_tfm == other.priority_tfm) and
|
||||
(self.heap_size == other.heap_size) and
|
||||
(self.stack_size == other.stack_size) and
|
||||
(self.entry_point == other.entry_point) and
|
||||
(self.source_files == other.source_files) and
|
||||
(self.mmio_regions == other.mmio_regions) and
|
||||
(self.rot_services == other.rot_services) and
|
||||
(self.extern_sids == other.extern_sids) and
|
||||
(self.irqs == other.irqs)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, manifest_file, skip_src=False):
|
||||
"""
|
||||
Load a partition manifest file
|
||||
|
||||
:param manifest_file: Manifest file path
|
||||
:param skip_src: Ignore the `source_files` entry
|
||||
:return: Manifest object
|
||||
"""
|
||||
|
||||
partition_schema_path = path_join(
|
||||
SCRIPT_DIR,
|
||||
'partition_description_schema.json'
|
||||
)
|
||||
with open(partition_schema_path) as schema_fh:
|
||||
partition_schema = json.load(schema_fh)
|
||||
|
||||
# Load partition manifest file.
|
||||
with open(manifest_file) as fh:
|
||||
manifest = json.load(fh)
|
||||
|
||||
validate(manifest, partition_schema)
|
||||
manifest_dir = os.path.dirname(manifest_file)
|
||||
|
||||
source_files = []
|
||||
if not skip_src:
|
||||
for src_file in manifest['source_files']:
|
||||
source_files.append(
|
||||
os.path.normpath(path_join(manifest_dir, src_file)))
|
||||
|
||||
mmio_regions = []
|
||||
for mmio_region in manifest.get('mmio_regions', []):
|
||||
mmio_regions.append(
|
||||
MmioRegion(partition_id=manifest['id'], **mmio_region))
|
||||
|
||||
rot_services = []
|
||||
for rot_srv in manifest.get('services', []):
|
||||
rot_services.append(RotService(**rot_srv))
|
||||
|
||||
irqs = []
|
||||
for irq in manifest.get('irqs', []):
|
||||
irqs.append(Irq(**irq))
|
||||
|
||||
return Manifest(
|
||||
manifest_file=manifest_file,
|
||||
name=manifest['name'],
|
||||
partition_id=_assert_int(manifest['id']),
|
||||
partition_type=manifest['type'],
|
||||
priority=manifest['priority'],
|
||||
heap_size=_assert_int(manifest['heap_size']),
|
||||
stack_size=_assert_int(manifest['stack_size']),
|
||||
entry_point=manifest['entry_point'],
|
||||
source_files=source_files,
|
||||
mmio_regions=mmio_regions,
|
||||
rot_services=rot_services,
|
||||
extern_sids=manifest.get('extern_sids', []),
|
||||
irqs=irqs
|
||||
)
|
||||
|
||||
@property
|
||||
def sids(self):
|
||||
return [rot_srv.name for rot_srv in self.rot_services]
|
||||
|
||||
@property
|
||||
def autogen_folder(self):
|
||||
return os.path.abspath(os.path.dirname(self.file))
|
||||
|
||||
def find_dependencies(self, manifests):
|
||||
"""
|
||||
Find other manifests which holds Root of Trust Services that
|
||||
are declared as extern in this manifest
|
||||
|
||||
:param manifests: list of manifests to filter
|
||||
:return: list of manifest's names that holds current
|
||||
extern Root of Trust Services
|
||||
"""
|
||||
|
||||
manifests = [man for man in manifests if man != self]
|
||||
extern_sids_set = set(self.extern_sids)
|
||||
return [manifest.name for manifest in manifests
|
||||
if extern_sids_set.intersection(set(manifest.sids))]
|
||||
|
||||
def templates_to_files(self, templates, templates_base, output_dir):
|
||||
"""
|
||||
Translates a list of partition templates to file names
|
||||
|
||||
:param templates: List of partition templates
|
||||
:param templates_base: Base directory of the templates
|
||||
:param output_dir: Output directory (Default is autogen folder property)
|
||||
:return: Dictionary of template to output file translation
|
||||
"""
|
||||
|
||||
generated_files = {}
|
||||
for t in templates:
|
||||
fname = os.path.relpath(t, templates_base)
|
||||
_tpl = fname.replace('NAME', self.name.lower())
|
||||
full_path = path_join(
|
||||
output_dir,
|
||||
os.path.splitext(_tpl)[0]
|
||||
)
|
||||
generated_files[t] = full_path
|
||||
|
||||
return generated_files
|
||||
|
||||
|
||||
def check_circular_call_dependencies(manifests):
|
||||
"""
|
||||
Check if there is a circular dependency between the partitions
|
||||
described by the manifests.
|
||||
A circular dependency might happen if there is a scenario in which a
|
||||
partition calls a Root of Trust Service in another partition which than
|
||||
calls another Root of Trust Service which resides in the
|
||||
originating partition.
|
||||
For example: Partition A has a Root of Trust Service A1 and extern sid B1,
|
||||
partition B has a Root of Trust Service B1 and extern sid A1.
|
||||
|
||||
:param manifests: List of the partition manifests.
|
||||
:return: True if a circular dependency exists, false otherwise.
|
||||
"""
|
||||
|
||||
# Construct a call graph.
|
||||
call_graph = {}
|
||||
for manifest in manifests:
|
||||
call_graph[manifest.name] = {
|
||||
'calls': manifest.find_dependencies(manifests),
|
||||
'called_by': set()
|
||||
}
|
||||
for manifest_name in call_graph:
|
||||
for called in call_graph[manifest_name]['calls']:
|
||||
call_graph[called]['called_by'].add(manifest_name)
|
||||
|
||||
# Run topological sort on the call graph.
|
||||
while len(call_graph) > 0:
|
||||
# Find all the nodes that aren't called by anyone and
|
||||
# therefore can be removed.
|
||||
nodes_to_remove = [x for x in list(call_graph.keys()) if
|
||||
len(call_graph[x]['called_by']) == 0]
|
||||
|
||||
# If no node can be removed we have a circle.
|
||||
if not nodes_to_remove:
|
||||
return True
|
||||
|
||||
# Remove the nodes.
|
||||
for node in nodes_to_remove:
|
||||
for called in call_graph[node]['calls']:
|
||||
call_graph[called]['called_by'].remove(node)
|
||||
call_graph.pop(node)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def validate_partition_manifests(manifests):
|
||||
"""
|
||||
Check the correctness of the manifests list
|
||||
(no conflicts, no missing elements, etc.)
|
||||
|
||||
:param manifests: List of the partition manifests
|
||||
"""
|
||||
for manifest in manifests:
|
||||
assert isinstance(manifest, Manifest)
|
||||
|
||||
partitions_names = {}
|
||||
partitions_ids = {}
|
||||
rot_service_ids = {}
|
||||
rot_service_names = {}
|
||||
rot_service_signals = {}
|
||||
irq_signals = {}
|
||||
irq_numbers = {}
|
||||
all_extern_sids = set()
|
||||
spe_contained_manifests = []
|
||||
|
||||
for manifest in manifests:
|
||||
# Make sure the partition names are unique.
|
||||
if manifest.name in partitions_names:
|
||||
raise ValueError(
|
||||
'Partition name {} is not unique, '
|
||||
'found in both {} and {}.'.format(
|
||||
manifest.name,
|
||||
partitions_names[manifest.name],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
partitions_names[manifest.name] = manifest.file
|
||||
|
||||
# Make sure the partition ID's are unique.
|
||||
if manifest.id in partitions_ids:
|
||||
raise ValueError(
|
||||
'Partition id {} is not unique, '
|
||||
'found in both {} and {}.'.format(
|
||||
manifest.id,
|
||||
partitions_ids[manifest.id],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
partitions_ids[manifest.id] = manifest.file
|
||||
|
||||
is_nspe_callabale = False
|
||||
|
||||
# Make sure all the Root of Trust Service IDs and signals are unique.
|
||||
for rot_service in manifest.rot_services:
|
||||
if rot_service.name in rot_service_names:
|
||||
raise ValueError(
|
||||
'Root of Trust Service name {} is found '
|
||||
'in both {} and {}.'.format(
|
||||
rot_service.name,
|
||||
rot_service_names[rot_service.name],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
rot_service_names[rot_service.name] = manifest.file
|
||||
|
||||
if rot_service.signal in rot_service_signals:
|
||||
raise ValueError(
|
||||
'Root of Trust Service signal {} is found '
|
||||
'in both {} and {}.'.format(
|
||||
rot_service.signal,
|
||||
rot_service_signals[rot_service.signal],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
rot_service_signals[rot_service.signal] = manifest.file
|
||||
|
||||
if rot_service.numeric_id in rot_service_ids:
|
||||
raise ValueError(
|
||||
'Root of Trust Service identifier {} is found '
|
||||
'in both {} and {}.'.format(
|
||||
rot_service.numeric_id,
|
||||
rot_service_ids[rot_service.numeric_id],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
rot_service_ids[rot_service.numeric_id] = manifest.file
|
||||
is_nspe_callabale |= rot_service.nspe_callable
|
||||
|
||||
if not is_nspe_callabale:
|
||||
spe_contained_manifests.append(manifest)
|
||||
|
||||
# Make sure all the IRQ signals and line-numbers are unique.
|
||||
for irq in manifest.irqs:
|
||||
if irq.signal in irq_signals:
|
||||
raise ValueError(
|
||||
'IRQ signal {} is found in both {} and {}.'.format(
|
||||
irq.signal,
|
||||
irq_signals[irq.signal],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
irq_signals[irq.signal] = manifest.file
|
||||
|
||||
if irq.line_num in irq_numbers:
|
||||
raise ValueError(
|
||||
'IRQ line number {} is found in both {} and {}.'.format(
|
||||
irq.line_num,
|
||||
irq_numbers[irq.line_num],
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
irq_numbers[irq.line_num] = manifest.file
|
||||
|
||||
all_extern_sids.update(manifest.extern_sids)
|
||||
|
||||
# Check that all the external SIDs can be found.
|
||||
declared_sids = set(rot_service_names.keys())
|
||||
for manifest in manifests:
|
||||
extern_sids = set(manifest.extern_sids)
|
||||
if not extern_sids.issubset(declared_sids):
|
||||
missing_sids = extern_sids.difference(declared_sids)
|
||||
raise ValueError(
|
||||
"External SID(s) {} required by {} can't be found in "
|
||||
"any partition manifest.".format(
|
||||
', '.join(missing_sids), manifest.file)
|
||||
)
|
||||
|
||||
if check_circular_call_dependencies(manifests):
|
||||
raise ValueError(
|
||||
"Detected a circular call dependency between the partitions.")
|
||||
|
||||
for manifest in spe_contained_manifests:
|
||||
rot_services = set([service.name for service in manifest.rot_services])
|
||||
if not rot_services.intersection(all_extern_sids) and len(
|
||||
manifest.irqs) == 0:
|
||||
raise ValueError(
|
||||
'Partition {} (defined by {}) is not accessible from NSPE '
|
||||
'and not referenced by any other partition.'.format(
|
||||
manifest.name,
|
||||
manifest.file
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def is_test_manifest(manifest):
|
||||
return 'TESTS' in manifest
|
||||
|
||||
|
||||
def is_service_manifest(manifest):
|
||||
return not is_test_manifest(manifest)
|
||||
|
||||
|
||||
def manifests_discovery(root_dirs, ignore_paths):
|
||||
service_manifest_files = set()
|
||||
test_manifest_files = set()
|
||||
for root_dir in root_dirs:
|
||||
for root, dirs, files in os.walk(root_dir, followlinks=True):
|
||||
# Filters paths if they are inside one of the ignore paths
|
||||
if next((True for igp in ignore_paths if igp in root), False):
|
||||
continue
|
||||
|
||||
to_add = [path_join(root, f) for f in
|
||||
fnmatch.filter(files, MANIFEST_FILE_PATTERN)]
|
||||
service_manifest_files.update(filter(is_service_manifest, to_add))
|
||||
test_manifest_files.update(filter(is_test_manifest, to_add))
|
||||
|
||||
service_manifest_files = sorted(list(service_manifest_files))
|
||||
test_manifest_files = sorted(list(test_manifest_files))
|
||||
return service_manifest_files, test_manifest_files
|
||||
|
||||
|
||||
def parse_manifests(manifests_files):
|
||||
region_list = []
|
||||
manifests = []
|
||||
for manifest_file in manifests_files:
|
||||
manifest_obj = Manifest.from_json(manifest_file)
|
||||
manifests.append(manifest_obj)
|
||||
for region in manifest_obj.mmio_regions:
|
||||
region_list.append(region)
|
||||
|
||||
return manifests, region_list
|
||||
|
||||
|
||||
def generate_source_files(
|
||||
templates,
|
||||
render_args,
|
||||
extra_filters=None
|
||||
):
|
||||
"""
|
||||
Generate SPM common C code from manifests using given templates
|
||||
|
||||
:param templates: Dictionary of template and their auto-generated products
|
||||
:param render_args: Dictionary of arguments that should be passed to render
|
||||
:param extra_filters: Dictionary of extra filters to use in the rendering
|
||||
process
|
||||
:return: Path to generated folder containing common generated files
|
||||
"""
|
||||
|
||||
rendered_files = []
|
||||
templates_dirs = list(
|
||||
set([os.path.dirname(path) for path in templates])
|
||||
)
|
||||
template_files = {os.path.basename(t): t for t in templates}
|
||||
|
||||
# Load templates for the code generation.
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(templates_dirs),
|
||||
lstrip_blocks=True,
|
||||
trim_blocks=True,
|
||||
undefined=StrictUndefined
|
||||
)
|
||||
if extra_filters:
|
||||
env.filters.update(extra_filters)
|
||||
|
||||
for tf in template_files:
|
||||
template = env.get_template(tf)
|
||||
rendered_files.append(
|
||||
(templates[template_files[tf]], template.render(**render_args)))
|
||||
rendered_file_dir = os.path.dirname(templates[template_files[tf]])
|
||||
if not os.path.exists(rendered_file_dir):
|
||||
os.makedirs(rendered_file_dir)
|
||||
|
||||
for fname, data in rendered_files:
|
||||
output_folder = os.path.dirname(fname)
|
||||
if not os.path.isdir(output_folder):
|
||||
os.makedirs(output_folder)
|
||||
with open(fname, 'wt') as fh:
|
||||
fh.write(data)
|
|
@ -1,196 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "schema for a partition description.",
|
||||
"type": "object",
|
||||
"required": ["name", "type", "priority", "id", "entry_point", "stack_size", "heap_size", "source_files"],
|
||||
"anyOf": [
|
||||
{"required" : ["services"]},
|
||||
{"required" : ["irqs"]}
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Alphanumeric C macro for referring to a partition. (all capital)",
|
||||
"$ref": "#/definitions/c_macro"
|
||||
},
|
||||
"type": {
|
||||
"description": "Whether the partition is unprivileged or part of the trusted computing base.",
|
||||
"enum": ["APPLICATION-ROT", "PSA-ROT"]
|
||||
},
|
||||
"priority": {
|
||||
"description": "Partition task priority.",
|
||||
"enum": ["LOW", "NORMAL", "HIGH"]
|
||||
},
|
||||
"id": {
|
||||
"description": "Partition numeric unique positive identifier. (must be a positive 8 bytes hex string)",
|
||||
"type": "string",
|
||||
"pattern": "^0x[0-7][0-9a-fA-F]{7}$"
|
||||
},
|
||||
"entry_point": {
|
||||
"description": "C symbol name of the partition's entry point. (unmangled, use extern C if needed)",
|
||||
"$ref": "#/definitions/c_symbol"
|
||||
},
|
||||
"stack_size": {
|
||||
"description": "Partition's task stack size in bytes.",
|
||||
"$ref": "#/definitions/positive_integer_or_hex_string"
|
||||
},
|
||||
"heap_size": {
|
||||
"description": "Partition's task heap size in bytes.",
|
||||
"$ref": "#/definitions/positive_integer_or_hex_string"
|
||||
},
|
||||
"mmio_regions": {
|
||||
"description": "List of Memory-Mapped IO region objects which the partition has access to.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{
|
||||
"$ref": "#/definitions/named_region"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/numbered_region"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"services": {
|
||||
"description": "List of RoT Service objects which the partition implements.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/service"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"extern_sids": {
|
||||
"description": "List of SID which the partition code depends on and allowed to access.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/c_macro"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"source_files": {
|
||||
"description": "List of source files relative to PSA Manifest file. A Secure Partition is built from explicit file list.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9-_./]+$"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"irqs": {
|
||||
"description": "List of IRQ objects which the partition implements.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/irq"
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"c_macro": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z_][A-Z0-9_]*$"
|
||||
},
|
||||
"c_symbol": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
|
||||
},
|
||||
"hex_string": {
|
||||
"type": "string",
|
||||
"pattern": "^0x(0*[1-9a-fA-F][0-9a-fA-F]*)$",
|
||||
"minLength": 3,
|
||||
"maxLength": 10
|
||||
},
|
||||
"positive_integer": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": true,
|
||||
"minimum": 0
|
||||
},
|
||||
"positive_integer_or_hex_string": {
|
||||
"oneOf": [{
|
||||
"$ref": "#/definitions/positive_integer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/hex_string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"named_region": {
|
||||
"description": "MMIO region which is described by it's C macro name and access permissions.",
|
||||
"required": ["name", "permission"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Alphanumeric C macro for referring to the region.",
|
||||
"$ref": "#/definitions/c_macro"
|
||||
},
|
||||
"permission": {
|
||||
"description": "Access permissions for the region.",
|
||||
"enum": ["READ-ONLY", "READ-WRITE"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"numbered_region": {
|
||||
"description": "MMIO region which is described by it's base address, size and access permissions.",
|
||||
"required": ["base", "size", "permission"],
|
||||
"properties": {
|
||||
"base": {
|
||||
"description": "The base address of the region.",
|
||||
"$ref": "#/definitions/hex_string"
|
||||
},
|
||||
"size": {
|
||||
"description": "Size in bytes of the region.",
|
||||
"$ref": "#/definitions/positive_integer_or_hex_string"
|
||||
},
|
||||
"permission": {
|
||||
"description": "Access permissions for the region.",
|
||||
"enum": ["READ-ONLY", "READ-WRITE"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"required": ["name", "identifier", "non_secure_clients", "signal"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Alphanumeric C macro for referring to a RoT Service from source code (all capital)",
|
||||
"$ref": "#/definitions/c_macro"
|
||||
},
|
||||
"identifier": {
|
||||
"description": "The integer value of the NAME field",
|
||||
"$ref": "#/definitions/positive_integer_or_hex_string"
|
||||
},
|
||||
"non_secure_clients": {
|
||||
"description": "Denote whether the RoT Service is exposed to non-secure clients.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"signal": {
|
||||
"description": "Alphanumeric C macro for referring to the RoT Service's signal value. (all capital)",
|
||||
"$ref": "#/definitions/c_macro"
|
||||
},
|
||||
"minor_version": {
|
||||
"description": "Optional: Minor version number of the RoT Service's interface.",
|
||||
"$ref": "#/definitions/positive_integer",
|
||||
"default": 1
|
||||
},
|
||||
"minor_policy": {
|
||||
"description": "Optional: Minor version policy to apply on connections to the RoT Service.",
|
||||
"enum": ["STRICT", "RELAXED"],
|
||||
"default": "STRICT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"irq": {
|
||||
"required": ["line_num", "signal"],
|
||||
"properties": {
|
||||
"line_num": {
|
||||
"description": "Interrupt line number for registering to ISR table entry and enable/disable the specific IRQ once received.",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"signal": {
|
||||
"description": "Alphanumeric C macro for referring to the IRQ's signal value. (all capital)",
|
||||
"$ref": "#/definitions/c_macro"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,389 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2017-2018 ARM Limited
|
||||
#
|
||||
# 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 subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
FNULL = open(os.devnull, 'w')
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
os.pardir, os.pardir))
|
||||
sys.path.insert(0, ROOT)
|
||||
from tools.toolchains import TOOLCHAIN_CLASSES
|
||||
from tools.targets import Target, TARGET_MAP, TARGET_NAMES
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='[%(name)s] %(asctime)s: %(message)s.',
|
||||
datefmt='%H:%M:%S')
|
||||
logger = logging.getLogger('PSA release tool')
|
||||
subprocess_output = None
|
||||
subprocess_err = None
|
||||
|
||||
MAKE_PY_LOCATTION = os.path.join(ROOT, 'tools', 'make.py')
|
||||
TEST_PY_LOCATTION = os.path.join(ROOT, 'tools', 'test.py')
|
||||
TFM_MBED_APP = os.path.join(ROOT, 'tools', 'psa', 'tfm', 'mbed_app.json')
|
||||
PSA_TESTS = {
|
||||
'*psa-spm_smoke': ['USE_PSA_TEST_PARTITIONS', 'USE_SMOKE_TESTS_PART1'],
|
||||
'*psa-spm_client': ['USE_PSA_TEST_PARTITIONS', 'USE_CLIENT_TESTS_PART1'],
|
||||
'*psa-spm_server': ['USE_PSA_TEST_PARTITIONS', 'USE_SERVER_TESTS_PART1',
|
||||
'USE_SERVER_TESTS_PART2'],
|
||||
'*psa-crypto_access_control': ['USE_PSA_TEST_PARTITIONS',
|
||||
'USE_CRYPTO_ACL_TEST']
|
||||
}
|
||||
PSA_AUTOGEN_LOCATION = os.path.join(ROOT, 'components', 'TARGET_PSA')
|
||||
|
||||
def _psa_backend(target):
|
||||
"""
|
||||
Returns a target PSA backend.
|
||||
|
||||
:param target: Target name as in targets.json
|
||||
:return: PSA backend as string (TFM/MBED_SPM)
|
||||
"""
|
||||
return 'TFM' if 'TFM' in Target.get_target(target).labels else 'MBED_SPM'
|
||||
|
||||
|
||||
def _get_target_info(target, toolchain):
|
||||
"""
|
||||
Creates a PSA target tuple with default toolchain and
|
||||
artifact delivery directory.
|
||||
|
||||
:param target: Target name.
|
||||
:return: tuple (target, toolchain, delivery directory).
|
||||
"""
|
||||
delivery_dir = os.path.join(ROOT, 'targets',
|
||||
TARGET_MAP[target].delivery_dir)
|
||||
|
||||
if not os.path.exists(delivery_dir):
|
||||
raise Exception("{} does not have delivery_dir".format(target))
|
||||
|
||||
if toolchain:
|
||||
if toolchain not in TARGET_MAP[target].supported_toolchains:
|
||||
raise Exception("Toolchain {} is not supported by {}".format(toolchain, TARGET_MAP[target].name))
|
||||
return tuple([TARGET_MAP[target].name,
|
||||
toolchain,
|
||||
delivery_dir])
|
||||
else:
|
||||
return tuple([TARGET_MAP[target].name,
|
||||
TARGET_MAP[target].default_toolchain,
|
||||
delivery_dir])
|
||||
|
||||
|
||||
def _get_psa_secure_targets_list():
|
||||
"""
|
||||
Creates a list of PSA secure targets.
|
||||
|
||||
:return: List of PSA secure targets.
|
||||
"""
|
||||
return [str(t) for t in TARGET_NAMES if
|
||||
Target.get_target(t).is_PSA_secure_target]
|
||||
|
||||
|
||||
def verbose_check_call(cmd, check_call=True):
|
||||
"""
|
||||
Calls a shell command and logs the call.
|
||||
|
||||
:param cmd: command to run as a list
|
||||
:param check_call: choose subprocess method (call/check_call)
|
||||
:return: return code of the executed command
|
||||
"""
|
||||
logger.info('Running: {}'.format(' '.join(cmd)))
|
||||
if check_call:
|
||||
return subprocess.check_call(cmd, stdout=subprocess_output,
|
||||
stderr=subprocess_err)
|
||||
|
||||
return subprocess.call(cmd, stdout=subprocess_output, stderr=subprocess_err)
|
||||
|
||||
|
||||
def get_mbed_official_psa_release(target=None, toolchain=None):
|
||||
"""
|
||||
Creates a list of PSA targets with default toolchain and
|
||||
artifact delivery directory.
|
||||
|
||||
:param target: Ask for specific target, None for all targets.
|
||||
:return: List of tuples (target, toolchain, delivery directory).
|
||||
"""
|
||||
psa_secure_targets = _get_psa_secure_targets_list()
|
||||
logger.debug("Found the following PSA targets: {}".format(
|
||||
', '.join(psa_secure_targets)))
|
||||
if target is not None:
|
||||
return [_get_target_info(target, toolchain)]
|
||||
|
||||
return [_get_target_info(t, toolchain) for t in psa_secure_targets]
|
||||
|
||||
|
||||
def create_mbed_ignore(build_dir):
|
||||
"""
|
||||
Creates a .mbedignore file in a given directory.
|
||||
|
||||
:param build_dir: Directory to create .mbedignore file.
|
||||
"""
|
||||
logger.debug('Created .mbedignore in {}'.format(build_dir))
|
||||
with open(os.path.join(build_dir, '.mbedignore'), 'w') as f:
|
||||
f.write('*\n')
|
||||
|
||||
|
||||
def build_tests(target, toolchain, profile, args):
|
||||
"""
|
||||
Builds secure images for tests.
|
||||
|
||||
:param target: target to be built.
|
||||
:param toolchain: toolchain to be used.
|
||||
:param profile: build profile.
|
||||
:param args: list of extra arguments.
|
||||
"""
|
||||
build_dir = os.path.join(ROOT, 'BUILD', 'tests', target)
|
||||
if os.path.exists(build_dir):
|
||||
logger.info("BUILD directory deleted: {}".format(build_dir))
|
||||
shutil.rmtree(build_dir)
|
||||
|
||||
for test in PSA_TESTS.keys():
|
||||
logger.info(
|
||||
"Building tests image({}) for {} using {} with {} profile".format(
|
||||
test, target, toolchain, profile))
|
||||
|
||||
test_defines = ['-D{}'.format(define) for define in PSA_TESTS[test]]
|
||||
cmd = [
|
||||
sys.executable, TEST_PY_LOCATTION,
|
||||
'--greentea',
|
||||
'--profile', profile,
|
||||
'-t', toolchain,
|
||||
'-m', target,
|
||||
'--source', ROOT,
|
||||
'--build', build_dir,
|
||||
'--test-spec', os.path.join(build_dir, 'test_spec.json'),
|
||||
'--build-data', os.path.join(build_dir, 'build_data.json'),
|
||||
'-n', test] + test_defines + args
|
||||
|
||||
if _psa_backend(target) is 'TFM':
|
||||
cmd += ['--app-config', TFM_MBED_APP]
|
||||
|
||||
verbose_check_call(cmd)
|
||||
logger.info(
|
||||
"Finished Building tests image({}) for {}".format(test, target))
|
||||
|
||||
|
||||
def build_default_image(target, toolchain, profile, args):
|
||||
"""
|
||||
Builds the default secure image.
|
||||
|
||||
:param target: target to be built.
|
||||
:param toolchain: toolchain to be used.
|
||||
:param profile: build profile.
|
||||
:param args: list of extra arguments.
|
||||
"""
|
||||
logger.info("Building default image for {} using {} with {} profile".format(
|
||||
target, toolchain, profile))
|
||||
|
||||
build_dir = os.path.join(ROOT, 'BUILD', target)
|
||||
if os.path.exists(build_dir):
|
||||
logger.info("BUILD directory deleted: {}".format(build_dir))
|
||||
shutil.rmtree(build_dir)
|
||||
|
||||
cmd = [
|
||||
sys.executable, MAKE_PY_LOCATTION,
|
||||
'-t', toolchain,
|
||||
'-m', target,
|
||||
'--profile', profile,
|
||||
'--source', ROOT,
|
||||
'--build', build_dir] + args
|
||||
|
||||
if _psa_backend(target) is 'TFM':
|
||||
cmd += ['--app-config', TFM_MBED_APP]
|
||||
else:
|
||||
cmd += ['--artifact-name', 'psa_release_1.0']
|
||||
|
||||
verbose_check_call(cmd)
|
||||
logger.info(
|
||||
"Finished building default image for {} successfully".format(target))
|
||||
|
||||
|
||||
def commit_binaries(target, delivery_dir, toolchain):
|
||||
"""
|
||||
Commits changes in secure binaries.
|
||||
|
||||
:param target: Target name.
|
||||
:param delivery_dir: Secure images should be moved to this folder
|
||||
by the build system.
|
||||
"""
|
||||
changes_made = verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'diff', '--exit-code', '--quiet',
|
||||
delivery_dir], check_call=False)
|
||||
|
||||
if changes_made:
|
||||
logger.info("Change in images for {} has been detected".format(target))
|
||||
verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'add', os.path.relpath(delivery_dir, ROOT)])
|
||||
|
||||
logger.info("Committing images for {}".format(target))
|
||||
commit_message = '--message="Update secure binaries for %s (%s)"' % (
|
||||
target, toolchain)
|
||||
verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'commit',
|
||||
commit_message])
|
||||
else:
|
||||
logger.info("No changes detected in {}, Skipping commit".format(target))
|
||||
|
||||
def commit_psa_autogen():
|
||||
"""
|
||||
Commit changes related to auto-generated PSA components and services
|
||||
"""
|
||||
changes_made = verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'diff', '--exit-code', '--quiet',
|
||||
PSA_AUTOGEN_LOCATION], check_call=False)
|
||||
|
||||
if changes_made:
|
||||
logger.info("Change in PSA auto-generated files has been detected")
|
||||
verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'add', PSA_AUTOGEN_LOCATION])
|
||||
|
||||
logger.info("Committing changes...")
|
||||
commit_message = ('--message=Update PSA auto-generated components and '
|
||||
'services')
|
||||
verbose_check_call([
|
||||
'git',
|
||||
'-C', ROOT,
|
||||
'commit',
|
||||
commit_message])
|
||||
else:
|
||||
logger.info("No changes has been detected for PSA autogen, "
|
||||
"Skipping commit")
|
||||
|
||||
def build_psa_platform(target, toolchain, delivery_dir, debug, git_commit,
|
||||
skip_tests, args):
|
||||
"""
|
||||
Calls the correct build function and commits if requested.
|
||||
|
||||
:param target: Target name.
|
||||
:param toolchain: Toolchain to be used.
|
||||
:param delivery_dir: Artifact directory, where images should be placed.
|
||||
:param debug: Build with debug profile.
|
||||
:param git_commit: Commit the changes.
|
||||
:param skip_tests: skip the test images build phase.
|
||||
:param args: list of extra arguments.
|
||||
"""
|
||||
profile = 'debug' if debug else 'release'
|
||||
if not skip_tests:
|
||||
build_tests(target, toolchain, profile, args)
|
||||
|
||||
build_default_image(target, toolchain, profile, args)
|
||||
if git_commit:
|
||||
commit_binaries(target, delivery_dir, toolchain)
|
||||
commit_psa_autogen()
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-m", "--mcu",
|
||||
help="build for the given MCU",
|
||||
default=None,
|
||||
choices=_get_psa_secure_targets_list(),
|
||||
metavar="MCU")
|
||||
|
||||
parser.add_argument("-t", "--tc",
|
||||
help="build for the given tool chain (default is default_toolchain)",
|
||||
default=None)
|
||||
|
||||
parser.add_argument("-d", "--debug",
|
||||
help="set build profile to debug",
|
||||
action="store_true",
|
||||
default=False)
|
||||
|
||||
parser.add_argument('-q', '--quiet',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="No Build log will be printed")
|
||||
|
||||
parser.add_argument('-l', '--list',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Print supported PSA secure targets")
|
||||
|
||||
parser.add_argument("--commit",
|
||||
help="create a git commit for each platform",
|
||||
action="store_true",
|
||||
default=False)
|
||||
|
||||
parser.add_argument('--skip-tests',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="skip the test build phase")
|
||||
|
||||
parser.add_argument('-x', '--extra',
|
||||
dest='extra_args',
|
||||
default=[],
|
||||
nargs=argparse.REMAINDER,
|
||||
help="additional build parameters")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def prep_build_dir():
|
||||
build_dir = os.path.join(ROOT, 'BUILD')
|
||||
if not os.path.exists(build_dir):
|
||||
logger.info("BUILD directory created in {}".format(build_dir))
|
||||
os.makedirs(build_dir)
|
||||
create_mbed_ignore(build_dir)
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
options = parser.parse_args()
|
||||
if options.quiet:
|
||||
logger.setLevel(logging.INFO)
|
||||
global subprocess_output, subprocess_err
|
||||
subprocess_output = FNULL
|
||||
subprocess_err = subprocess.STDOUT
|
||||
|
||||
if options.list:
|
||||
logger.info("Available platforms are: {}".format(
|
||||
', '.join([t for t in _get_psa_secure_targets_list()])))
|
||||
return
|
||||
|
||||
prep_build_dir()
|
||||
psa_platforms_list = get_mbed_official_psa_release(options.mcu, options.tc)
|
||||
logger.info("Building the following platforms: {}".format(
|
||||
', '.join([t[0] for t in psa_platforms_list])))
|
||||
|
||||
toolchains_set = set([t[1] for t in psa_platforms_list])
|
||||
for tc in toolchains_set:
|
||||
if not TOOLCHAIN_CLASSES[tc].check_executable():
|
||||
raise Exception("Toolchain {} was not found in PATH".format(tc))
|
||||
|
||||
for target, tc, directory in psa_platforms_list:
|
||||
build_psa_platform(target, tc, directory, options.debug,
|
||||
options.commit, options.skip_tests,
|
||||
options.extra_args)
|
||||
|
||||
logger.info("Finished Updating PSA images")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,37 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "Secure Partition ID definitions",
|
||||
"template": "tools/psa/templates/tfm_partition_defs.inc.tpl",
|
||||
"output": "TARGET_TFM/COMPONENT_SPE/inc/tfm_partition_defs.inc"
|
||||
},
|
||||
{
|
||||
"name": "Secure Partition declarations",
|
||||
"template": "tools/psa/templates/tfm_partition_list.inc.tpl",
|
||||
"output": "TARGET_TFM/COMPONENT_SPE/inc/tfm_partition_list.inc"
|
||||
},
|
||||
{
|
||||
"name": "Secure Service list",
|
||||
"template": "tools/psa/templates/tfm_service_list.inc.tpl",
|
||||
"output": "TARGET_TFM/COMPONENT_SPE/inc/tfm_service_list.inc"
|
||||
},
|
||||
{
|
||||
"name": "Secure Service signals list",
|
||||
"template": "tools/psa/templates/tfm_spm_signal_defs.h.tpl",
|
||||
"output": "TARGET_TFM/COMPONENT_SPE/inc/tfm_spm_signal_defs.h"
|
||||
},
|
||||
{
|
||||
"name": "mbed-SPM database",
|
||||
"template": "tools/psa/templates/psa_setup.c.tpl",
|
||||
"output": "TARGET_MBED_SPM/COMPONENT_SPE/psa_setup.c"
|
||||
},
|
||||
{
|
||||
"name": "Mappings from RoT Service names to SIDs",
|
||||
"template": "tools/psa/templates/sid.h.tpl",
|
||||
"output": "services/inc/autogen_sid.h"
|
||||
},
|
||||
{
|
||||
"name": "Details partition defines and structures",
|
||||
"template": "tools/psa/templates/mbed_spm_partitions.h.tpl",
|
||||
"output": "services/inc/mbed_spm_partitions.h"
|
||||
}
|
||||
]
|
|
@ -1,104 +0,0 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MBED_SPM_PARTITIONS_H___
|
||||
#define __MBED_SPM_PARTITIONS_H___
|
||||
|
||||
{% macro do_parition(partition) -%}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} defines
|
||||
* -------------------------------------------------------------------------- */
|
||||
#define {{partition.name|upper}}_ID {{partition.id}}
|
||||
|
||||
{% if partition.rot_services|count > 0 %}
|
||||
#define {{partition.name|upper}}_ROT_SRV_COUNT ({{partition.rot_services|count}}UL)
|
||||
{% endif %}
|
||||
#define {{partition.name|upper}}_EXT_ROT_SRV_COUNT ({{partition.extern_sids|count}}UL)
|
||||
|
||||
{% for irq in partition.irqs %}
|
||||
#define {{irq.signal|upper}}_POS ({{loop.index + 3 }}UL)
|
||||
#define {{irq.signal|upper}} (1UL << {{irq.signal|upper}}_POS)
|
||||
{% endfor %}
|
||||
|
||||
{% if partition.irqs|count > 0 %}
|
||||
#define {{partition.name|upper}}_WAIT_ANY_IRQ_MSK (\
|
||||
{% for irq in partition.irqs %}
|
||||
{{irq.signal|upper}}{{")" if loop.last else " | \\"}}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
#define {{partition.name|upper}}_WAIT_ANY_IRQ_MSK (0)
|
||||
{% endif %}
|
||||
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
#define {{rot_srv.signal|upper}}_POS ({{loop.index + 3 + partition.irqs|count}}UL)
|
||||
#define {{rot_srv.signal|upper}} (1UL << {{rot_srv.signal|upper}}_POS)
|
||||
{% endfor %}
|
||||
|
||||
{% if partition.rot_services|count > 0 %}
|
||||
#define {{partition.name|upper}}_WAIT_ANY_SID_MSK (\
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
{{rot_srv.signal|upper}}{{")" if loop.last else " | \\"}}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
#define {{partition.name|upper}}_WAIT_ANY_SID_MSK (0)
|
||||
{% endif %}
|
||||
|
||||
{% if partition.irqs|count > 0 %}
|
||||
uint32_t spm_{{partition.name|lower}}_signal_to_irq_mapper(uint32_t signal);
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
{# ------------------ macro do_parition(partition) -------------------------- #}
|
||||
|
||||
/****************** Common definitions ****************************************/
|
||||
|
||||
/* PSA reserved event flags */
|
||||
#define PSA_RESERVED1_POS (1UL)
|
||||
#define PSA_RESERVED1_MSK (1UL << PSA_RESERVED1_POS)
|
||||
|
||||
#define PSA_RESERVED2_POS (2UL)
|
||||
#define PSA_RESERVED2_MSK (1UL << PSA_RESERVED2_POS)
|
||||
|
||||
/****************** Service Partitions ****************************************/
|
||||
|
||||
{% for partition in service_partitions %}
|
||||
{{ do_parition(partition) }}
|
||||
{% endfor %}
|
||||
|
||||
/****************** Test Partitions *******************************************/
|
||||
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for test_partition in test_partitions %}
|
||||
#ifdef USE_{{test_partition.name|upper}}
|
||||
{{ do_parition(test_partition) }}
|
||||
#endif // USE_{{test_partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
#endif // __MBED_SPM_PARTITIONS_H___
|
||||
{# End of file #}
|
|
@ -1,281 +0,0 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#include "cmsis.h"
|
||||
#include "rtx_os.h"
|
||||
|
||||
#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */
|
||||
|
||||
#include "spm_panic.h"
|
||||
#include "spm_internal.h"
|
||||
#include "handles_manager.h"
|
||||
#include "mbed_spm_partitions.h"
|
||||
|
||||
#include "psa_manifest/sid.h"
|
||||
|
||||
extern spm_db_t g_spm;
|
||||
|
||||
{% macro do_parition(partition) -%}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} declarations
|
||||
* -------------------------------------------------------------------------- */
|
||||
MBED_ALIGN(8) static uint8_t {{partition.name|lower}}_thread_stack[{{partition.stack_size}}] = {0};
|
||||
|
||||
static osRtxThread_t {{partition.name|lower}}_thread_cb = {0};
|
||||
static const osThreadAttr_t {{partition.name|lower}}_thread_attr = {
|
||||
.name = "{{partition.name|lower}}",
|
||||
.attr_bits = 0,
|
||||
.cb_mem = &{{partition.name|lower}}_thread_cb,
|
||||
.cb_size = sizeof({{partition.name|lower}}_thread_cb),
|
||||
.stack_mem = {{partition.name|lower}}_thread_stack,
|
||||
.stack_size = {{partition.stack_size}},
|
||||
.priority = {{partition.priority_mbed}},
|
||||
.tz_module = 0,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
static osRtxMutex_t {{partition.name|lower}}_mutex = {0};
|
||||
static const osMutexAttr_t {{partition.name|lower}}_mutex_attr = {
|
||||
.name = "{{partition.name|lower}}_mutex",
|
||||
.attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust,
|
||||
.cb_mem = &{{partition.name|lower}}_mutex,
|
||||
.cb_size = sizeof({{partition.name|lower}}_mutex),
|
||||
};
|
||||
|
||||
{% if partition.rot_services|count > 0 %}
|
||||
spm_rot_service_t {{partition.name|lower}}_rot_services[] = {
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
{
|
||||
.sid = {{rot_srv.name|upper}},
|
||||
.mask = {{rot_srv.signal|upper}},
|
||||
.partition = NULL,
|
||||
.min_version = {{rot_srv.minor_version}},
|
||||
.min_version_policy = PSA_MINOR_VERSION_POLICY_{{rot_srv.minor_policy|upper}},
|
||||
{% if rot_srv.nspe_callable %}
|
||||
.allow_nspe = true,
|
||||
{% else %}
|
||||
.allow_nspe = false,
|
||||
{% endif %}
|
||||
.queue = {
|
||||
.head = NULL,
|
||||
.tail = NULL
|
||||
}
|
||||
},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
{% endif %}
|
||||
{% if partition.extern_sids|count > 0 %}
|
||||
/* External SIDs used by {{partition.name}} */
|
||||
const uint32_t {{partition.name|lower}}_external_sids[{{partition.extern_sids|count}}] = {
|
||||
{% for sid in partition.extern_sids %}
|
||||
{{sid|upper}},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
{% endif %}
|
||||
{% if partition.irqs|count > 0 %}
|
||||
// Mapper function from irq signal to interupts number
|
||||
IRQn_Type spm_{{partition.name|lower}}_signal_to_irq_mapper(uint32_t signal)
|
||||
{
|
||||
SPM_ASSERT({{partition.name|upper}}_WAIT_ANY_IRQ_MSK & signal);
|
||||
switch(signal){
|
||||
{% for irq in partition.irqs %}
|
||||
case {{ irq.signal }}:
|
||||
return (IRQn_Type){{irq.line_num}};
|
||||
break;
|
||||
{% endfor %}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SPM_PANIC("Unknown signal number %lu", signal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
{% for irq in partition.irqs %}
|
||||
// ISR handler for interrupt {irq.line_num}
|
||||
void spm_irq_{{irq.signal}}_{{partition.name|lower}}(void)
|
||||
{
|
||||
spm_partition_t *partition = NULL;
|
||||
for (uint32_t i = 0; i < g_spm.partition_count; ++i) {
|
||||
if (g_spm.partitions[i].partition_id == {{partition.name|upper}}_ID) {
|
||||
partition = &(g_spm.partitions[i]);
|
||||
}
|
||||
}
|
||||
SPM_ASSERT(partition);
|
||||
|
||||
NVIC_DisableIRQ((IRQn_Type){{irq.line_num}}); // will be enabled by psa_eoi()
|
||||
osThreadFlagsSet(partition->thread_id, {{irq.signal|upper}}); // notify partition
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
extern void {{partition.entry_point}}(void *ptr);
|
||||
|
||||
void {{partition.name|lower}}_init(spm_partition_t *partition)
|
||||
{
|
||||
if (NULL == partition) {
|
||||
SPM_PANIC("partition is NULL!\n");
|
||||
}
|
||||
|
||||
partition->mutex = osMutexNew(&{{partition.name|lower}}_mutex_attr);
|
||||
if (NULL == partition->mutex) {
|
||||
SPM_PANIC("Failed to create mutex for secure partition {{partition.name|lower}}!\n");
|
||||
}
|
||||
|
||||
{% if partition.rot_services|count > 0 %}
|
||||
for (uint32_t i = 0; i < {{partition.name|upper}}_ROT_SRV_COUNT; ++i) {
|
||||
{{partition.name|lower}}_rot_services[i].partition = partition;
|
||||
}
|
||||
partition->rot_services = {{partition.name|lower}}_rot_services;
|
||||
{% else %}
|
||||
partition->rot_services = NULL;
|
||||
{% endif %}
|
||||
|
||||
partition->thread_id = osThreadNew({{partition.entry_point}}, NULL, &{{partition.name|lower}}_thread_attr);
|
||||
if (NULL == partition->thread_id) {
|
||||
SPM_PANIC("Failed to create start main thread of partition {{partition.name|lower}}!\n");
|
||||
}
|
||||
}
|
||||
|
||||
{%- endmacro %}
|
||||
{# -------------- macro do_parition(partition) ----------------------------- #}
|
||||
/****************** Service Partitions ****************************************/
|
||||
|
||||
{% for partition in service_partitions %}
|
||||
{{do_parition(partition)}}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
/****************** Test Partitions *******************************************/
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for test_partition in test_partitions %}
|
||||
#ifdef USE_{{test_partition.name|upper}}
|
||||
{{ do_parition(test_partition) }}
|
||||
|
||||
#endif // USE_{{test_partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{# -------------- spm_db_entry(partition) ----------------------------------- #}
|
||||
{% macro spm_db_entry(partition) -%}
|
||||
|
||||
/* {{partition.name|upper}} */
|
||||
{
|
||||
.partition_id = {{partition.name|upper}}_ID,
|
||||
.thread_id = 0,
|
||||
.flags = {{partition.name|upper}}_WAIT_ANY_SID_MSK | {{partition.name|upper}}_WAIT_ANY_IRQ_MSK,
|
||||
.rot_services = NULL,
|
||||
{% if partition.rot_services|count > 0 %}
|
||||
.rot_services_count = {{partition.name|upper}}_ROT_SRV_COUNT,
|
||||
{% else %}
|
||||
.rot_services_count = 0,
|
||||
{% endif %}
|
||||
{% if partition.extern_sids|count > 0 %}
|
||||
.extern_sids = {{partition.name|lower}}_external_sids,
|
||||
{% else %}
|
||||
.extern_sids = NULL,
|
||||
{% endif %}
|
||||
.extern_sids_count = {{partition.name|upper}}_EXT_ROT_SRV_COUNT,
|
||||
{% if partition.irqs|count > 0 %}
|
||||
.irq_mapper = spm_{{partition.name|lower}}_signal_to_irq_mapper,
|
||||
{% else %}
|
||||
.irq_mapper = NULL,
|
||||
{% endif %}
|
||||
},
|
||||
{%- endmacro %}
|
||||
{# -------------- spm_db_entry(partition) ----------------------------------- #}
|
||||
/****************** SPM DB initialization *************************************/
|
||||
spm_partition_t g_partitions[] = {
|
||||
{% for partition in service_partitions %}
|
||||
{{spm_db_entry(partition)}}
|
||||
|
||||
{% endfor %}
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for test_partition in test_partitions %}
|
||||
#ifdef USE_{{test_partition.name|upper}} {{ spm_db_entry(test_partition) }}
|
||||
#endif // USE_{{test_partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
};
|
||||
|
||||
/****************** MMIO regions **********************************************/
|
||||
{% if regions|count > 0 %}
|
||||
/****************** Sanity checks *********************************************/
|
||||
/* Check all the defined memory regions for overlapping. */
|
||||
{% for region_pair in region_pair_list %}
|
||||
MBED_STATIC_ASSERT(
|
||||
((uintptr_t)({{region_pair[0].base}}) + {{region_pair[0].size}} - 1 < (uintptr_t)({{region_pair[1].base}})) ||
|
||||
((uintptr_t)({{region_pair[1].base}}) + {{region_pair[1].size}} - 1 < (uintptr_t)({{region_pair[0].base}})),
|
||||
"The region with base {{region_pair[0].base}} and size {{region_pair[0].size}} overlaps with the region with base {{region_pair[1].base}} and size {{region_pair[1].size}}!");
|
||||
|
||||
{% endfor %}
|
||||
/****************** MMIO regions definition ***********************************/
|
||||
/* A list of all the memory regions. */
|
||||
const mem_region_t mem_regions[] = {
|
||||
{% for region in regions %}
|
||||
{ (uint32_t)({{region.base}}), {{region.size}}, {{region.permission}}, {{region.partition_id}} },
|
||||
{% endfor %}
|
||||
};
|
||||
{% else %}
|
||||
const mem_region_t *mem_regions = NULL;
|
||||
{% endif %}
|
||||
const uint32_t mem_region_count = {{regions|count}};
|
||||
|
||||
/****************** Partitions init function *********************************/
|
||||
uint32_t init_partitions(spm_partition_t **partitions)
|
||||
{
|
||||
uint32_t partition_idx = 0;
|
||||
|
||||
if (NULL == partitions) {
|
||||
SPM_PANIC("partitions is NULL!\n");
|
||||
}
|
||||
|
||||
{% for partition in service_partitions %}
|
||||
{{partition.name|lower}}_init(&(g_partitions[partition_idx++]));
|
||||
{% endfor %}
|
||||
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for test_partition in test_partitions %}
|
||||
#ifdef USE_{{test_partition.name|upper}}
|
||||
{{test_partition.name|lower}}_init(&(g_partitions[partition_idx++]));
|
||||
#endif // USE_{{test_partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
*partitions = g_partitions;
|
||||
return partition_idx;
|
||||
}
|
||||
|
||||
{# End of file #}
|
|
@ -1,47 +0,0 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
{% macro parition_sid(partition) -%}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} Service IDs
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
#define {{rot_srv.name|upper}} {{rot_srv.id}}
|
||||
{% endfor %}
|
||||
|
||||
{%- endmacro %}
|
||||
{# -------------- macro parition_sid(partition) ---------------------------- #}
|
||||
|
||||
/****************** Service Partitions ****************************************/
|
||||
|
||||
{% for partition in service_partitions %}
|
||||
{{parition_sid(partition)}}
|
||||
{% endfor %}
|
||||
/****************** Test Partitions *******************************************/
|
||||
|
||||
{% for partition in test_partitions %}
|
||||
{{parition_sid(partition)}}
|
||||
{% endfor %}
|
||||
{# End of file #}
|
|
@ -1,56 +0,0 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __TFM_PARTITION_DEFS_INC__
|
||||
#define __TFM_PARTITION_DEFS_INC__
|
||||
|
||||
/*************************** Service Partitions *******************************/
|
||||
|
||||
{% for partition in service_partitions %}
|
||||
{% set partition_loop = loop %}
|
||||
#define {{partition.name|upper}}_ID (TFM_SP_BASE + {{ partition_loop.index0 }})
|
||||
{% endfor %}
|
||||
|
||||
/*************************** Test Partitions **********************************/
|
||||
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for partition in test_partitions %}
|
||||
{% set partition_loop = loop %}
|
||||
#ifdef USE_{{partition.name|upper}}
|
||||
#define {{partition.name|upper}}_ID (TFM_SP_BASE + {{service_partitions|count}} + {{ partition_loop.index0 }})
|
||||
#endif
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
#define TFM_MAX_USER_PARTITIONS ({{service_partitions|count}} + {{test_partitions|count}})
|
||||
#else
|
||||
#define TFM_MAX_USER_PARTITIONS ({{service_partitions|count}})
|
||||
#endif
|
||||
|
||||
#endif // __TFM_PARTITION_DEFS_INC__
|
||||
{# End of file #}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __TFM_PARTITION_LIST_INC__
|
||||
#define __TFM_PARTITION_LIST_INC__
|
||||
|
||||
/*************************** Service Partitions *******************************/
|
||||
{% for partition in service_partitions %}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}}
|
||||
* -------------------------------------------------------------------------- */
|
||||
PARTITION_DECLARE({{partition.name|upper}}, 0
|
||||
| SPM_PART_FLAG_IPC
|
||||
, "{{partition.type}}", {{partition.id}}, {{partition.priority_tfm}}, {{partition.stack_size}});
|
||||
PARTITION_ADD_INIT_FUNC({{partition.name|upper}}, {{partition.entry_point}});
|
||||
|
||||
{% endfor %}
|
||||
/*************************** Test Partitions **********************************/
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for partition in test_partitions %}
|
||||
#ifdef USE_{{partition.name|upper}}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}}
|
||||
* -------------------------------------------------------------------------- */
|
||||
PARTITION_DECLARE({{partition.name|upper}}, 0
|
||||
| SPM_PART_FLAG_IPC
|
||||
, "{{partition.type}}", {{partition.id}}, {{partition.priority_tfm}}, {{partition.stack_size}});
|
||||
PARTITION_ADD_INIT_FUNC({{partition.name|upper}}, {{partition.entry_point}});
|
||||
#endif // USE_{{partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
#endif // __TFM_PARTITION_LIST_INC__
|
||||
{# End of file #}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __TFM_SERVICE_LIST_INC__
|
||||
#define __TFM_SERVICE_LIST_INC__
|
||||
|
||||
/*************************** Service Partitions *******************************/
|
||||
{% for partition in service_partitions %}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} Services
|
||||
* -------------------------------------------------------------------------- */
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
{"{{rot_srv.name|upper}}", {{partition.name|upper}}_ID, {{rot_srv.signal|upper}}, {{rot_srv.id}}, {% if rot_srv.nspe_callable %}true{% else %}false{% endif %}, {{rot_srv.minor_version}}, TFM_VERSION_POLICY_{{rot_srv.minor_policy|upper}}},
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
/*************************** Test Partitions **********************************/
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for partition in test_partitions %}
|
||||
#ifdef USE_{{partition.name|upper}}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} Services
|
||||
* -------------------------------------------------------------------------- */
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
{"{{rot_srv.name|upper}}", {{partition.name|upper}}_ID, {{rot_srv.signal|upper}}, {{rot_srv.id}}, {% if rot_srv.nspe_callable %}true{% else %}false{% endif %}, {{rot_srv.minor_version}}, TFM_VERSION_POLICY_{{rot_srv.minor_policy|upper}}},
|
||||
{% endfor %}
|
||||
#endif // USE_{{partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
#endif // __TFM_SERVICE_LIST_INC__
|
||||
{# End of file #}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* THIS FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/psa/generate_partition_code.py Version {{script_ver}}
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __TFM_SPM_SIGNAL_DEFS_H__
|
||||
#define __TFM_SPM_SIGNAL_DEFS_H__
|
||||
|
||||
/*************************** Service Partitions *******************************/
|
||||
{% for partition in service_partitions %}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} Signals
|
||||
* -------------------------------------------------------------------------- */
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
#define {{rot_srv.signal|upper}}_POS ({{loop.index + 3}}UL)
|
||||
#define {{rot_srv.signal|upper}} (1UL << {{rot_srv.signal|upper}}_POS)
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
/*************************** Test Partitions **********************************/
|
||||
#ifdef USE_PSA_TEST_PARTITIONS
|
||||
|
||||
{% for partition in test_partitions %}
|
||||
#ifdef USE_{{partition.name|upper}}
|
||||
/* -----------------------------------------------------------------------------
|
||||
* {{partition.name|upper}} Signals
|
||||
* -------------------------------------------------------------------------- */
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
#define {{rot_srv.signal|upper}}_POS ({{loop.index + 3}}UL)
|
||||
#define {{rot_srv.signal|upper}} (1UL << {{rot_srv.signal|upper}}_POS)
|
||||
{% endfor %}
|
||||
#endif // USE_{{partition.name|upper}}
|
||||
|
||||
{% endfor %}
|
||||
#endif // USE_PSA_TEST_PARTITIONS
|
||||
|
||||
#endif // __TFM_SPM_SIGNAL_DEFS_H__
|
||||
{# End of file #}
|
|
@ -1,21 +0,0 @@
|
|||
# Copyright (c) 2017-2018 ARM Limited
|
||||
#
|
||||
# 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.
|
||||
|
||||
from .assemble import Assembly
|
||||
|
||||
__all__ = [
|
||||
'Assembly'
|
||||
]
|
|
@ -1,105 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
#
|
||||
# Copyright 2017 Linaro Limited
|
||||
# Copyright (c) 2017-2018, Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Assemble multiple images into a single image that can be flashed on the device.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import shutil
|
||||
|
||||
offset_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_OFFSET\s+((0x)?[0-9a-fA-F]+)")
|
||||
size_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_MAX_SIZE\s+((0x)?[0-9a-fA-F]+)")
|
||||
|
||||
class Assembly():
|
||||
def __init__(self, layout_path, output):
|
||||
self.output = output
|
||||
self.layout_path = layout_path
|
||||
self.find_slots()
|
||||
try:
|
||||
os.unlink(output)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
def find_slots(self):
|
||||
offsets = {}
|
||||
sizes = {}
|
||||
|
||||
if os.path.isabs(self.layout_path):
|
||||
configFile = self.layout_path
|
||||
else:
|
||||
scriptsDir = os.path.dirname(os.path.abspath(__file__))
|
||||
configFile = os.path.join(scriptsDir, self.layout_path)
|
||||
|
||||
with open(configFile, 'r') as fd:
|
||||
for line in fd:
|
||||
m = offset_re.match(line)
|
||||
if m is not None:
|
||||
offsets[m.group(1)] = int(m.group(2), 0)
|
||||
m = size_re.match(line)
|
||||
if m is not None:
|
||||
sizes[m.group(1)] = int(m.group(2), 0)
|
||||
|
||||
if 'SECURE' not in offsets:
|
||||
raise Exception("Image config does not have secure partition")
|
||||
|
||||
if 'NON_SECURE' not in offsets:
|
||||
raise Exception("Image config does not have non-secure partition")
|
||||
|
||||
self.offsets = offsets
|
||||
self.sizes = sizes
|
||||
|
||||
def add_image(self, source, partition):
|
||||
with open(self.output, 'ab') as ofd:
|
||||
ofd.seek(0, os.SEEK_END)
|
||||
pos = ofd.tell()
|
||||
if pos > self.offsets[partition]:
|
||||
raise Exception("Partitions not in order, unsupported")
|
||||
if pos < self.offsets[partition]:
|
||||
ofd.write(b'\xFF' * (self.offsets[partition] - pos))
|
||||
statinfo = os.stat(source)
|
||||
if statinfo.st_size > self.sizes[partition]:
|
||||
raise Exception("Image {} is too large for partition".format(source))
|
||||
with open(source, 'rb') as rfd:
|
||||
shutil.copyfileobj(rfd, ofd, 0x10000)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-l', '--layout', required=True,
|
||||
help='Location of the memory layout file')
|
||||
parser.add_argument('-s', '--secure', required=True,
|
||||
help='Unsigned secure image')
|
||||
parser.add_argument('-n', '--non_secure',
|
||||
help='Unsigned non-secure image')
|
||||
parser.add_argument('-o', '--output', required=True,
|
||||
help='Filename to write full image to')
|
||||
|
||||
args = parser.parse_args()
|
||||
output = Assembly(args.layout, args.output)
|
||||
|
||||
|
||||
output.add_image(args.secure, "SECURE")
|
||||
output.add_image(args.non_secure, "NON_SECURE")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,177 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
#
|
||||
# Copyright 2017 Linaro Limited
|
||||
# Copyright (c) 2018, Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
from .imgtool_lib import keys
|
||||
from .imgtool_lib import image
|
||||
from .imgtool_lib import version
|
||||
import sys
|
||||
|
||||
def find_load_address(args):
|
||||
load_address_re = re.compile(r"^#define\sIMAGE_LOAD_ADDRESS\s+(0x[0-9a-fA-F]+)")
|
||||
|
||||
if os.path.isabs(args.layout):
|
||||
configFile = args.layout
|
||||
else:
|
||||
scriptsDir = os.path.dirname(os.path.abspath(__file__))
|
||||
configFile = os.path.join(scriptsDir, args.layout)
|
||||
|
||||
ramLoadAddress = None
|
||||
with open(configFile, 'r') as flash_layout_file:
|
||||
for line in flash_layout_file:
|
||||
m = load_address_re.match(line)
|
||||
if m is not None:
|
||||
ramLoadAddress = int(m.group(1), 0)
|
||||
print("**[INFO]** Writing load address from the macro in "
|
||||
"flash_layout.h to the image header.. "
|
||||
+ hex(ramLoadAddress)
|
||||
+ " (dec. " + str(ramLoadAddress) + ")")
|
||||
break
|
||||
return ramLoadAddress
|
||||
|
||||
# Returns the last version number if present, or None if not
|
||||
def get_last_version(path):
|
||||
if (os.path.isfile(path) == False): # Version file not present
|
||||
return None
|
||||
else: # Version file is present, check it has a valid number inside it
|
||||
with open(path, "r") as oldFile:
|
||||
fileContents = oldFile.read()
|
||||
if version.version_re.match(fileContents): # number is valid
|
||||
return version.decode_version(fileContents)
|
||||
else:
|
||||
return None
|
||||
|
||||
def next_version_number(args, defaultVersion, path):
|
||||
newVersion = None
|
||||
if (version.compare(args.version, defaultVersion) == 0): # Default version
|
||||
lastVersion = get_last_version(path)
|
||||
if (lastVersion is not None):
|
||||
newVersion = version.increment_build_num(lastVersion)
|
||||
else:
|
||||
newVersion = version.increment_build_num(defaultVersion)
|
||||
else: # Version number has been explicitly provided (not using the default)
|
||||
newVersion = args.version
|
||||
versionString = "{a}.{b}.{c}+{d}".format(
|
||||
a=str(newVersion.major),
|
||||
b=str(newVersion.minor),
|
||||
c=str(newVersion.revision),
|
||||
d=str(newVersion.build)
|
||||
)
|
||||
with open(path, "w") as newFile:
|
||||
newFile.write(versionString)
|
||||
print("**[INFO]** Image version number set to " + versionString)
|
||||
return newVersion
|
||||
|
||||
def gen_rsa2048(args):
|
||||
keys.RSA2048.generate().export_private(args.key)
|
||||
|
||||
keygens = {
|
||||
'rsa-2048': gen_rsa2048, }
|
||||
|
||||
def do_keygen(args):
|
||||
if args.type not in keygens:
|
||||
msg = "Unexpected key type: {}".format(args.type)
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
keygens[args.type](args)
|
||||
|
||||
def do_getpub(args):
|
||||
key = keys.load(args.key)
|
||||
if args.lang == 'c':
|
||||
key.emit_c()
|
||||
else:
|
||||
msg = "Unsupported language, valid are: c"
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
|
||||
def do_sign(args):
|
||||
if args.rsa_pkcs1_15:
|
||||
keys.sign_rsa_pss = False
|
||||
img = image.Image.load(args.infile,
|
||||
version=next_version_number(args,
|
||||
version.decode_version("0"),
|
||||
"lastVerNum.txt"),
|
||||
header_size=args.header_size,
|
||||
included_header=args.included_header,
|
||||
pad=args.pad)
|
||||
key = keys.load(args.key) if args.key else None
|
||||
img.sign(key, find_load_address(args))
|
||||
|
||||
if args.pad:
|
||||
img.pad_to(args.pad, args.align)
|
||||
|
||||
img.save(args.outfile)
|
||||
|
||||
subcmds = {
|
||||
'keygen': do_keygen,
|
||||
'getpub': do_getpub,
|
||||
'sign': do_sign, }
|
||||
|
||||
def alignment_value(text):
|
||||
value = int(text)
|
||||
if value not in [1, 2, 4, 8]:
|
||||
msg = "{} must be one of 1, 2, 4 or 8".format(value)
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
def intparse(text):
|
||||
"""Parse a command line argument as an integer.
|
||||
|
||||
Accepts 0x and other prefixes to allow other bases to be used."""
|
||||
return int(text, 0)
|
||||
|
||||
def args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subs = parser.add_subparsers(help='subcommand help', dest='subcmd')
|
||||
|
||||
keygenp = subs.add_parser('keygen', help='Generate pub/private keypair')
|
||||
keygenp.add_argument('-k', '--key', metavar='filename', required=True)
|
||||
keygenp.add_argument('-t', '--type', metavar='type',
|
||||
choices=keygens.keys(), required=True)
|
||||
|
||||
getpub = subs.add_parser('getpub', help='Get public key from keypair')
|
||||
getpub.add_argument('-k', '--key', metavar='filename', required=True)
|
||||
getpub.add_argument('-l', '--lang', metavar='lang', default='c')
|
||||
|
||||
sign = subs.add_parser('sign', help='Sign an image with a private key')
|
||||
sign.add_argument('--layout', required=True,
|
||||
help='Location of the memory layout file')
|
||||
sign.add_argument('-k', '--key', metavar='filename')
|
||||
sign.add_argument("--align", type=alignment_value, required=True)
|
||||
sign.add_argument("-v", "--version", type=version.decode_version,
|
||||
default="0.0.0+0")
|
||||
sign.add_argument("-H", "--header-size", type=intparse, required=True)
|
||||
sign.add_argument("--included-header", default=False, action='store_true',
|
||||
help='Image has gap for header')
|
||||
sign.add_argument("--pad", type=intparse,
|
||||
help='Pad image to this many bytes, adding trailer magic')
|
||||
sign.add_argument("--rsa-pkcs1-15",
|
||||
help='Use old PKCS#1 v1.5 signature algorithm',
|
||||
default=False, action='store_true')
|
||||
sign.add_argument("infile")
|
||||
sign.add_argument("outfile")
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.subcmd is None:
|
||||
print('Must specify a subcommand', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
subcmds[args.subcmd](args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args()
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright 2017 Linaro Limited
|
||||
#
|
||||
# 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.
|
||||
|
||||
# This file is intentionally empty.
|
||||
#
|
||||
# The __init__.py files are required to make Python treat the directories as
|
||||
# containing packages.
|
|
@ -1,179 +0,0 @@
|
|||
# Copyright 2017 Linaro Limited
|
||||
# Copyright (c) 2018, Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Image signing and management.
|
||||
"""
|
||||
|
||||
from . import version as versmod
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
IMAGE_MAGIC = 0x96f3b83d
|
||||
IMAGE_HEADER_SIZE = 32
|
||||
|
||||
# Image header flags.
|
||||
IMAGE_F = {
|
||||
'PIC': 0x0000001,
|
||||
'NON_BOOTABLE': 0x0000010,
|
||||
'RAM_LOAD': 0x0000020, }
|
||||
TLV_VALUES = {
|
||||
'KEYHASH': 0x01,
|
||||
'SHA256' : 0x10,
|
||||
'RSA2048': 0x20, }
|
||||
|
||||
TLV_INFO_SIZE = 4
|
||||
TLV_INFO_MAGIC = 0x6907
|
||||
|
||||
# Sizes of the image trailer, depending on flash write size.
|
||||
trailer_sizes = {
|
||||
write_size: 128 * 3 * write_size + 8 * 2 + 16
|
||||
for write_size in [1, 2, 4, 8]
|
||||
}
|
||||
|
||||
boot_magic = bytearray([
|
||||
0x77, 0xc2, 0x95, 0xf3,
|
||||
0x60, 0xd2, 0xef, 0x7f,
|
||||
0x35, 0x52, 0x50, 0x0f,
|
||||
0x2c, 0xb6, 0x79, 0x80, ])
|
||||
|
||||
class TLV():
|
||||
def __init__(self):
|
||||
self.buf = bytearray()
|
||||
|
||||
def add(self, kind, payload):
|
||||
"""Add a TLV record. Kind should be a string found in TLV_VALUES above."""
|
||||
buf = struct.pack('<BBH', TLV_VALUES[kind], 0, len(payload))
|
||||
self.buf += buf
|
||||
self.buf += payload
|
||||
|
||||
def get(self):
|
||||
header = struct.pack('<HH', TLV_INFO_MAGIC, TLV_INFO_SIZE + len(self.buf))
|
||||
return header + bytes(self.buf)
|
||||
|
||||
class Image():
|
||||
@classmethod
|
||||
def load(cls, path, included_header=False, **kwargs):
|
||||
"""Load an image from a given file"""
|
||||
with open(path, 'rb') as f:
|
||||
payload = f.read()
|
||||
obj = cls(**kwargs)
|
||||
obj.payload = payload
|
||||
|
||||
# Add the image header if needed.
|
||||
if not included_header and obj.header_size > 0:
|
||||
obj.payload = (b'\000' * obj.header_size) + obj.payload
|
||||
|
||||
obj.check()
|
||||
return obj
|
||||
|
||||
def __init__(self, version, header_size=IMAGE_HEADER_SIZE, pad=0):
|
||||
self.version = version
|
||||
self.header_size = header_size or IMAGE_HEADER_SIZE
|
||||
self.pad = pad
|
||||
|
||||
def __repr__(self):
|
||||
return "<Image version={}, header_size={}, pad={}, payloadlen=0x{:x}>".format(
|
||||
self.version,
|
||||
self.header_size,
|
||||
self.pad,
|
||||
len(self.payload))
|
||||
|
||||
def save(self, path):
|
||||
with open(path, 'wb') as f:
|
||||
f.write(self.payload)
|
||||
|
||||
def check(self):
|
||||
"""Perform some sanity checking of the image."""
|
||||
# If there is a header requested, make sure that the image
|
||||
# starts with all zeros.
|
||||
if self.header_size > 0:
|
||||
if any(v != 0 and v != b'\000' for v in self.payload[0:self.header_size]):
|
||||
raise Exception("Padding requested, but image does not start with zeros")
|
||||
|
||||
def sign(self, key, ramLoadAddress):
|
||||
self.add_header(key, ramLoadAddress)
|
||||
|
||||
tlv = TLV()
|
||||
|
||||
sha = hashlib.sha256()
|
||||
sha.update(self.payload)
|
||||
digest = sha.digest()
|
||||
|
||||
tlv.add('SHA256', digest)
|
||||
|
||||
if key is not None:
|
||||
pub = key.get_public_bytes()
|
||||
sha = hashlib.sha256()
|
||||
sha.update(pub)
|
||||
pubbytes = sha.digest()
|
||||
tlv.add('KEYHASH', pubbytes)
|
||||
|
||||
sig = key.sign(self.payload)
|
||||
tlv.add(key.sig_tlv(), sig)
|
||||
|
||||
self.payload += tlv.get()
|
||||
|
||||
def add_header(self, key, ramLoadAddress):
|
||||
"""Install the image header.
|
||||
|
||||
The key is needed to know the type of signature, and
|
||||
approximate the size of the signature."""
|
||||
|
||||
flags = 0
|
||||
if ramLoadAddress is not None:
|
||||
# add the load address flag to the header to indicate that an SRAM
|
||||
# load address macro has been defined
|
||||
flags |= IMAGE_F["RAM_LOAD"]
|
||||
|
||||
fmt = ('<' +
|
||||
# type ImageHdr struct {
|
||||
'I' + # Magic uint32
|
||||
'I' + # LoadAddr uint32
|
||||
'H' + # HdrSz uint16
|
||||
'H' + # Pad1 uint16
|
||||
'I' + # ImgSz uint32
|
||||
'I' + # Flags uint32
|
||||
'BBHI' + # Vers ImageVersion
|
||||
'I' # Pad2 uint32
|
||||
) # }
|
||||
assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE
|
||||
header = struct.pack(fmt,
|
||||
IMAGE_MAGIC,
|
||||
0 if (ramLoadAddress is None) else ramLoadAddress, # LoadAddr
|
||||
self.header_size,
|
||||
0, # Pad1
|
||||
len(self.payload) - self.header_size, # ImageSz
|
||||
flags, # Flags
|
||||
self.version.major,
|
||||
self.version.minor or 0,
|
||||
self.version.revision or 0,
|
||||
self.version.build or 0,
|
||||
0) # Pad2
|
||||
self.payload = bytearray(self.payload)
|
||||
self.payload[:len(header)] = header
|
||||
|
||||
def pad_to(self, size, align):
|
||||
"""Pad the image to the given size, with the given flash alignment."""
|
||||
tsize = trailer_sizes[align]
|
||||
padding = size - (len(self.payload) + tsize)
|
||||
if padding < 0:
|
||||
msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds requested size 0x{:x}".format(
|
||||
len(self.payload), tsize, size)
|
||||
raise Exception(msg)
|
||||
pbytes = b'\xff' * padding
|
||||
pbytes += b'\xff' * (tsize - len(boot_magic))
|
||||
pbytes += boot_magic
|
||||
self.payload += pbytes
|
|
@ -1,104 +0,0 @@
|
|||
# Copyright 2017 Linaro Limited
|
||||
# Copyright (c) 2017-2018, Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Cryptographic key management for imgtool.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
from Crypto.Hash import SHA256
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
|
||||
import hashlib
|
||||
from pyasn1.type import namedtype, univ
|
||||
from pyasn1.codec.der.encoder import encode
|
||||
|
||||
# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
|
||||
# the command line to support the older (less secure) PKCS1.5
|
||||
sign_rsa_pss = True
|
||||
|
||||
AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
|
||||
|
||||
class RSAPublicKey(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('modulus', univ.Integer()),
|
||||
namedtype.NamedType('publicExponent', univ.Integer()))
|
||||
|
||||
class RSA2048():
|
||||
def __init__(self, key):
|
||||
"""Construct an RSA2048 key with the given key data"""
|
||||
self.key = key
|
||||
|
||||
@staticmethod
|
||||
def generate():
|
||||
return RSA2048(RSA.generate(2048))
|
||||
|
||||
def export_private(self, path):
|
||||
with open(path, 'wb') as f:
|
||||
f.write(self.key.exportKey('PEM'))
|
||||
|
||||
def get_public_bytes(self):
|
||||
node = RSAPublicKey()
|
||||
node['modulus'] = self.key.n
|
||||
node['publicExponent'] = self.key.e
|
||||
return bytearray(encode(node))
|
||||
|
||||
def emit_c(self):
|
||||
print(AUTOGEN_MESSAGE)
|
||||
print("const unsigned char rsa_pub_key[] = {", end='')
|
||||
encoded = self.get_public_bytes()
|
||||
for count, b in enumerate(encoded):
|
||||
if count % 8 == 0:
|
||||
print("\n\t", end='')
|
||||
else:
|
||||
print(" ", end='')
|
||||
print("0x{:02x},".format(b), end='')
|
||||
print("\n};")
|
||||
print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
|
||||
|
||||
def sig_type(self):
|
||||
"""Return the type of this signature (as a string)"""
|
||||
if sign_rsa_pss:
|
||||
return "PKCS1_PSS_RSA2048_SHA256"
|
||||
else:
|
||||
return "PKCS15_RSA2048_SHA256"
|
||||
|
||||
def sig_len(self):
|
||||
return 256
|
||||
|
||||
def sig_tlv(self):
|
||||
return "RSA2048"
|
||||
|
||||
def sign(self, payload):
|
||||
converted_payload = bytes(payload)
|
||||
sha = SHA256.new(converted_payload)
|
||||
if sign_rsa_pss:
|
||||
signer = PKCS1_PSS.new(self.key)
|
||||
else:
|
||||
signer = PKCS1_v1_5.new(self.key)
|
||||
signature = signer.sign(sha)
|
||||
assert len(signature) == self.sig_len()
|
||||
return signature
|
||||
|
||||
def load(path):
|
||||
with open(path, 'rb') as f:
|
||||
pem = f.read()
|
||||
try:
|
||||
key = RSA.importKey(pem)
|
||||
if key.n.bit_length() != 2048:
|
||||
raise Exception("Unsupported RSA bit length, only 2048 supported")
|
||||
return RSA2048(key)
|
||||
except ValueError:
|
||||
raise Exception("Unsupported RSA key file")
|
|
@ -1,66 +0,0 @@
|
|||
# Copyright 2017 Linaro Limited
|
||||
# Copyright (c) 2018, Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Semi Semantic Versioning
|
||||
|
||||
Implements a subset of semantic versioning that is supportable by the image header.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import re
|
||||
|
||||
SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', 'build'])
|
||||
|
||||
def increment_build_num(lastVer):
|
||||
newVer = SemiSemVersion(lastVer.major, lastVer.minor, lastVer.revision, lastVer.build + 1)
|
||||
return newVer
|
||||
|
||||
# -1 if a is older than b; 0 if they're the same version; 1 if a is newer than b
|
||||
def compare(a, b):
|
||||
if (a.major > b.major): return 1
|
||||
elif (a.major < b.major): return -1
|
||||
else:
|
||||
if (a.minor > b.minor): return 1
|
||||
elif (a.minor < b.minor): return -1
|
||||
else:
|
||||
if (a.revision > b.revision): return 1
|
||||
elif (a.revision < b.revision): return -1
|
||||
else:
|
||||
if (a.build > b.build): return 1
|
||||
elif (a.build < b.build): return -1
|
||||
else: return 0
|
||||
|
||||
version_re = re.compile(r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""")
|
||||
def decode_version(text):
|
||||
"""Decode the version string, which should be of the form maj.min.rev+build"""
|
||||
m = version_re.match(text)
|
||||
if m:
|
||||
result = SemiSemVersion(
|
||||
int(m.group(1)) if m.group(1) else 0,
|
||||
int(m.group(3)) if m.group(3) else 0,
|
||||
int(m.group(5)) if m.group(5) else 0,
|
||||
int(m.group(7)) if m.group(7) else 0)
|
||||
return result
|
||||
else:
|
||||
msg = "Invalid version number, should be maj.min.rev+build with later parts optional"
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(decode_version("1.2"))
|
||||
print(decode_version("1.0"))
|
||||
print(decode_version("0.0.2+75"))
|
||||
print(decode_version("0.0.0+00"))
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"name": "tfm_build",
|
||||
"requires" : ["psa-services", "tfm", "tfm-s", "psa"],
|
||||
"macros": ["MBEDTLS_CIPHER_MODE_CTR", "MBEDTLS_CMAC_C"],
|
||||
"artifact_name": "tfm"
|
||||
}
|
|
@ -604,38 +604,3 @@ class Resources(object):
|
|||
for t in res_filter.file_types:
|
||||
self._file_refs[t] = set(filter(
|
||||
res_filter.predicate, self._file_refs[t]))
|
||||
|
||||
|
||||
class ResourceFilter(object):
|
||||
def __init__(self, file_types):
|
||||
self.file_types = file_types
|
||||
|
||||
def predicate(self, ref):
|
||||
raise NotImplemented
|
||||
|
||||
|
||||
class SpeOnlyResourceFilter(ResourceFilter):
|
||||
def __init__(self):
|
||||
ResourceFilter.__init__(
|
||||
self, [FileType.ASM_SRC, FileType.C_SRC, FileType.CPP_SRC])
|
||||
|
||||
def predicate(self, ref):
|
||||
return 'COMPONENT_SPE' in ref.name
|
||||
|
||||
|
||||
class OsAndSpeResourceFilter(ResourceFilter):
|
||||
def __init__(self):
|
||||
ResourceFilter.__init__(
|
||||
self, [FileType.ASM_SRC, FileType.C_SRC, FileType.CPP_SRC])
|
||||
|
||||
def predicate(self, ref):
|
||||
return ROOT in abspath(ref.name) or 'COMPONENT_SPE' in ref.name
|
||||
|
||||
|
||||
class PsaManifestResourceFilter(ResourceFilter):
|
||||
def __init__(self):
|
||||
ResourceFilter.__init__(
|
||||
self, [FileType.JSON])
|
||||
|
||||
def predicate(self, ref):
|
||||
return not ref.name.endswith('_psa.json')
|
||||
|
|
|
@ -30,7 +30,6 @@ from tools.settings import ROOT
|
|||
from tools.targets.LPC import patch
|
||||
from tools.paths import TOOLS_BOOTLOADERS
|
||||
from tools.utils import json_file_to_dict, NotSupportedException
|
||||
from tools.psa import find_secure_image
|
||||
|
||||
|
||||
__all__ = ["target", "TARGETS", "TARGET_MAP", "TARGET_NAMES", "CORE_LABELS",
|
||||
|
|
|
@ -43,8 +43,6 @@ from tools.utils import argparse_dir_not_parent
|
|||
from tools.utils import print_end_warnings
|
||||
from tools.settings import ROOT
|
||||
from tools.targets import Target
|
||||
from tools.psa import generate_psa_sources
|
||||
from tools.resources import OsAndSpeResourceFilter, SpeOnlyResourceFilter
|
||||
|
||||
def main():
|
||||
error = False
|
||||
|
@ -237,12 +235,6 @@ def main():
|
|||
profile = extract_profile(parser, options, internal_tc_name)
|
||||
try:
|
||||
resource_filter = None
|
||||
if target.is_PSA_secure_target:
|
||||
resource_filter = OsAndSpeResourceFilter()
|
||||
generate_psa_sources(
|
||||
source_dirs=base_source_paths,
|
||||
ignore_paths=[options.build_dir]
|
||||
)
|
||||
|
||||
# Build sources
|
||||
notify = TerminalNotifier(options.verbose, options.silent)
|
||||
|
@ -276,9 +268,6 @@ def main():
|
|||
|
||||
if not library_build_success:
|
||||
print("Failed to build library")
|
||||
else:
|
||||
if target.is_PSA_secure_target:
|
||||
resource_filter = SpeOnlyResourceFilter()
|
||||
else:
|
||||
resource_filter = None
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
"""
|
||||
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
|
||||
"""
|
|
@ -1,732 +0,0 @@
|
|||
# Copyright (c) 2017-2018 ARM Limited
|
||||
#
|
||||
# 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.
|
||||
|
||||
manifests = [
|
||||
{
|
||||
'name': 'TEST_PARTITION',
|
||||
'id': "0x7FFFFFFF",
|
||||
"type": "APPLICATION-ROT",
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512, # 512 == 0x200
|
||||
'heap_size': 2048,
|
||||
'mmio_regions': [
|
||||
{
|
||||
'name': 'PERIPH1',
|
||||
'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'name': 'PERIPH2',
|
||||
'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'base': '0xCCCCCCCC',
|
||||
'size': 4096, 'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'base': '0xDDDDDDDD',
|
||||
'size': 33554432, 'permission': 'READ-WRITE'
|
||||
}
|
||||
],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_version': 1,
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
{
|
||||
'name': 'SID2',
|
||||
'identifier': '0x00000002',
|
||||
'signal': 'SID2',
|
||||
'minor_version': 2,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
],
|
||||
'source_files': ['src1.cpp', 'src2.cpp'],
|
||||
'irqs': [
|
||||
{"line_num": 20, "signal": "ISR20"},
|
||||
{"line_num": 21, "signal": "ISR21"}
|
||||
],
|
||||
'extern_sids': ['SID3', 'SID4']
|
||||
},
|
||||
{
|
||||
'name': 'TEST_PARTITION2',
|
||||
'id': "0x7FFFFFFE",
|
||||
"type": "APPLICATION-ROT",
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test2_main',
|
||||
'stack_size': 512, # 512 == 0x200
|
||||
'heap_size': 2048,
|
||||
'mmio_regions': [
|
||||
{
|
||||
'name': 'PERIPH1',
|
||||
'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'name': 'PERIPH3',
|
||||
'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'base': '0xAAAAAAAA',
|
||||
'size': 4096, 'permission': 'READ-ONLY'
|
||||
},
|
||||
{
|
||||
'base': '0xBBBBBBBB',
|
||||
'size': 33554432, 'permission': 'READ-WRITE'
|
||||
}
|
||||
],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID3',
|
||||
'identifier': '0x00000003',
|
||||
'signal': 'SID3',
|
||||
'minor_version': 5,
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
{
|
||||
'name': 'SID4',
|
||||
'identifier': '0x00000004',
|
||||
'signal': 'SID4',
|
||||
'minor_version': 12,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
],
|
||||
'source_files': ['src3.cpp', 'src4.cpp'],
|
||||
'irqs': [
|
||||
{"line_num": 22, "signal": "ISR22"},
|
||||
{"line_num": 23, "signal": "ISR23"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
manifests_for_circular_call_dependency_checks = [
|
||||
{
|
||||
'name': 'PARTITION1',
|
||||
'id': '0x7FFFFFFF',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src1.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID2',
|
||||
'identifier': '0x00000002',
|
||||
'signal': 'SID2',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
],
|
||||
'extern_sids': ['SID3', 'SID4']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION2',
|
||||
'id': '0x7FFFFFFE',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src2.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID3',
|
||||
'identifier': '0x00000003',
|
||||
'signal': 'SID3',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID4',
|
||||
'identifier': '0x00000004',
|
||||
'signal': 'SID4',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
],
|
||||
'extern_sids': ['SID1', 'SID2']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION3',
|
||||
'id': '0x7FFFFFFD',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src3.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID5',
|
||||
'identifier': '0x00000005',
|
||||
'signal': 'SID5',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
],
|
||||
'extern_sids': ['SID7']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION4',
|
||||
'id': '0x7FFFFFFC',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src4.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID6',
|
||||
'identifier': '0x00000006',
|
||||
'signal': 'SID6',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID7',
|
||||
'identifier': '0x00000007',
|
||||
'signal': 'SID7',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
],
|
||||
'extern_sids': ['SID9']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION5',
|
||||
'id': '0x7FFFFFFB',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src5.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID8',
|
||||
'identifier': '0x00000008',
|
||||
'signal': 'SID8',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID9',
|
||||
'identifier': '0x00000009',
|
||||
'signal': 'SID9',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
],
|
||||
'extern_sids': ['SID5']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION6',
|
||||
'id': '0x7FFFFFFA',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src6.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID10',
|
||||
'identifier': '0x0000000A',
|
||||
'signal': 'SID10',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID11',
|
||||
'identifier': '0x0000000B',
|
||||
'signal': 'SID11',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
],
|
||||
'extern_sids': ['SID7', 'SID5']
|
||||
},
|
||||
{
|
||||
'name': 'PARTITION7',
|
||||
'id': '0x7FFFFFF9',
|
||||
'type': 'APPLICATION-ROT',
|
||||
'priority': 'NORMAL',
|
||||
'entry_point': 'test_main',
|
||||
'stack_size': 512,
|
||||
'heap_size': 2048,
|
||||
'source_files': ['src6.cpp'],
|
||||
'services': [
|
||||
{
|
||||
'name': 'SID12',
|
||||
'identifier': '0x0000000C',
|
||||
'signal': 'SID12',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
invalid_minor_version_policy_rot_srv = [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_version': 1,
|
||||
'minor_policy': 'invalid_policy',
|
||||
'non_secure_clients': True
|
||||
}
|
||||
]
|
||||
|
||||
invalid_nspe_callable_rot_srv = [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_version': 1,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': 'invalid_value'
|
||||
}
|
||||
]
|
||||
|
||||
missing_nspe_callable_rot_srv = [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_version': 1,
|
||||
'minor_policy': 'STRICT'
|
||||
}
|
||||
]
|
||||
|
||||
duplicate_signal_rot_services = [
|
||||
{
|
||||
'name': 'SID3',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_version': 5,
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
{
|
||||
'name': 'SID4',
|
||||
'identifier': '0x00000002',
|
||||
'signal': 'SID2',
|
||||
'minor_version': 12,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
]
|
||||
|
||||
duplicate_identifier_rot_services = [
|
||||
{
|
||||
'name': 'SID3',
|
||||
'identifier': '0x00000003',
|
||||
'signal': 'SID3',
|
||||
'minor_version': 5,
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
{
|
||||
'name': 'SID4',
|
||||
'identifier': '0x00000002',
|
||||
'signal': 'SID4',
|
||||
'minor_version': 12,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': True
|
||||
},
|
||||
]
|
||||
|
||||
spe_contained_rot_services = [
|
||||
{
|
||||
'name': 'SID5',
|
||||
'identifier': '0x00000005',
|
||||
'signal': 'SID5',
|
||||
'minor_version': 5,
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': False
|
||||
},
|
||||
{
|
||||
'name': 'SID6',
|
||||
'identifier': '0x00000006',
|
||||
'signal': 'SID6',
|
||||
'minor_version': 12,
|
||||
'minor_policy': 'STRICT',
|
||||
'non_secure_clients': False
|
||||
}
|
||||
]
|
||||
|
||||
missing_minor_version_rot_srv = [
|
||||
{
|
||||
'name': 'SID1',
|
||||
'identifier': '0x00000001',
|
||||
'signal': 'SID1',
|
||||
'minor_policy': 'RELAXED',
|
||||
'non_secure_clients': True
|
||||
}
|
||||
]
|
||||
|
||||
missing_minor_version_policy_rot_srv = [
|
||||
{
|
||||
'name': 'SID2',
|
||||
'identifier': '0x00000002',
|
||||
'signal': 'SID2',
|
||||
'minor_version': 1,
|
||||
'non_secure_clients': True
|
||||
}
|
||||
]
|
||||
|
||||
missing_minor_completley_rot_srv = [
|
||||
{'name': 'SID2', 'identifier': '0x00000002', 'signal': 'SID2',
|
||||
'non_secure_clients': True}
|
||||
]
|
||||
|
||||
duplicate_signal_irqs = [
|
||||
{"line_num": 22, "signal": "ISR20"}
|
||||
]
|
||||
|
||||
duplicate_line_num_irqs = [
|
||||
{"line_num": 21, "signal": "ISR22"}
|
||||
]
|
||||
|
||||
invalid_mmioregion_base = {
|
||||
'base': 'str',
|
||||
'size': 4096,
|
||||
'permission': 'READ-ONLY'
|
||||
}
|
||||
|
||||
invalid_mmioregion_size = {
|
||||
'base': '0xEEEEEEEE',
|
||||
'size': 'str',
|
||||
'permission': 'READ-ONLY'
|
||||
}
|
||||
|
||||
test_mock_files = {
|
||||
'manifest1': 1,
|
||||
'manifest2': 2,
|
||||
'template_common1': 3,
|
||||
'template_common2': 4,
|
||||
'template_NAME_3': 5,
|
||||
'template_NAME_4': 6,
|
||||
'gen1': 7,
|
||||
'gen2': 8,
|
||||
'gen3': 9,
|
||||
'gen4': 10,
|
||||
'gen5': 11,
|
||||
'gen6': 12
|
||||
}
|
||||
|
||||
test_common_template = '''{
|
||||
"num_of_partitions": {{partitions|count}},
|
||||
"partition_names": [
|
||||
{% for partition in partitions %}
|
||||
"{{partition.name}}"{{"" if loop.last else ","}}
|
||||
{% endfor %}
|
||||
],
|
||||
"num_of_region_pairs": {{region_pair_list|count}}
|
||||
}
|
||||
'''
|
||||
|
||||
test_common_expected = '''{
|
||||
"num_of_partitions": 2,
|
||||
"partition_names": [
|
||||
"TEST_PARTITION",
|
||||
"TEST_PARTITION2"
|
||||
],
|
||||
"num_of_region_pairs": 28
|
||||
}
|
||||
'''
|
||||
|
||||
test_partition_template = '''{
|
||||
"name": "{{partition.name}}",
|
||||
"id": "0x{{"%0x"|format(partition.id|int)|upper}}",
|
||||
"type": "{{partition.type}}",
|
||||
"priority": "{{partition.priority_mbed|find_priority_key}}",
|
||||
"entry_point": "{{partition.entry_point}}",
|
||||
"stack_size": {{partition.stack_size}},
|
||||
"heap_size": {{partition.heap_size}},
|
||||
"mmio_regions": [
|
||||
{% for mmio in partition.mmio_regions %}
|
||||
{
|
||||
{% if mmio.size|int %}
|
||||
"base": "{{mmio.base}}",
|
||||
"size": {{mmio.size}},
|
||||
{% else %}
|
||||
"name": "{{mmio.base}}",
|
||||
{% endif %}
|
||||
"permission": "{{mmio.permission|find_permission_key}}"
|
||||
{{"}" if loop.last else "},"}}
|
||||
{% endfor %}
|
||||
],
|
||||
"services": [
|
||||
{% for rot_srv in partition.rot_services %}
|
||||
{
|
||||
"name": "{{rot_srv.name}}",
|
||||
"identifier": "{{rot_srv.id}}",
|
||||
"signal": "{{rot_srv.signal}}",
|
||||
"minor_version": {{rot_srv.minor_version}},
|
||||
"minor_policy": "{{rot_srv.minor_policy}}",
|
||||
"non_secure_clients": {{rot_srv.nspe_callable|lower}}
|
||||
{{"}" if loop.last else "},"}}
|
||||
{% endfor %}
|
||||
],
|
||||
{% if partition.extern_sids %}
|
||||
"extern_sids": [
|
||||
{% for ext_sid in partition.extern_sids %}
|
||||
"{{ext_sid}}"{{"" if loop.last else ","}}
|
||||
{% endfor %}
|
||||
],
|
||||
{% endif %}
|
||||
"source_files": [
|
||||
{% for src in partition.source_files %}
|
||||
"{{src|basename}}"{{"" if loop.last else ","}}
|
||||
{% endfor %}
|
||||
],
|
||||
"irqs": [
|
||||
{% for irq in partition.irqs %}
|
||||
{
|
||||
"line_num": {{irq.line_num}},
|
||||
"signal": "{{irq.signal}}"
|
||||
{{"}" if loop.last else "},"}}
|
||||
{% endfor %}
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
||||
exceeding_services = [
|
||||
{
|
||||
"name": "XSID1",
|
||||
"signal": "XSID1",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000009"
|
||||
},
|
||||
{
|
||||
"name": "XSID2",
|
||||
"signal": "XSID2",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000a"
|
||||
}, {
|
||||
"name": "XSID3",
|
||||
"signal": "XSID3",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000b"
|
||||
}, {
|
||||
"name": "XSID4",
|
||||
"signal": "XSID4",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000c"
|
||||
}, {
|
||||
"name": "XSID5",
|
||||
"signal": "XSID5",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000d"
|
||||
}, {
|
||||
"name": "XSID6",
|
||||
"signal": "XSID6",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000e"
|
||||
}, {
|
||||
"name": "XSID7",
|
||||
"signal": "XSID7",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000000f"
|
||||
}, {
|
||||
"name": "XSID8",
|
||||
"signal": "XSID8",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000010"
|
||||
}, {
|
||||
"name": "XSID9",
|
||||
"signal": "XSID9",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000011"
|
||||
}, {
|
||||
"name": "XSID10",
|
||||
"signal": "XSID10",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000012"
|
||||
}, {
|
||||
"name": "XSID11",
|
||||
"signal": "XSID11",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000013"
|
||||
}, {
|
||||
"name": "XSID12",
|
||||
"signal": "XSID12",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000014"
|
||||
}, {
|
||||
"name": "XSID13",
|
||||
"signal": "XSID13",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000015"
|
||||
}, {
|
||||
"name": "XSID14",
|
||||
"signal": "XSID14",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000016"
|
||||
}, {
|
||||
"name": "XSID15",
|
||||
"signal": "XSID15",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000017"
|
||||
}, {
|
||||
"name": "XSID16",
|
||||
"signal": "XSID16",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000018"
|
||||
}, {
|
||||
"name": "XSID17",
|
||||
"signal": "XSID17",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000019"
|
||||
}, {
|
||||
"name": "XSID18",
|
||||
"signal": "XSID18",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001a"
|
||||
}, {
|
||||
"name": "XSID19",
|
||||
"signal": "XSID19",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001b"
|
||||
}, {
|
||||
"name": "XSID20",
|
||||
"signal": "XSID20",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001c"
|
||||
}, {
|
||||
"name": "XSID21",
|
||||
"signal": "XSID21",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001d"
|
||||
}, {
|
||||
"name": "XSID22",
|
||||
"signal": "XSID22",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001e"
|
||||
}, {
|
||||
"name": "XSID23",
|
||||
"signal": "XSID23",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x0000001f"
|
||||
}, {
|
||||
"name": "XSID24",
|
||||
"signal": "XSID24",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000020"
|
||||
}, {
|
||||
"name": "XSID25",
|
||||
"signal": "XSID25",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000021"
|
||||
}, {
|
||||
"name": "XSID26",
|
||||
"signal": "XSID26",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000022"
|
||||
}, {
|
||||
"name": "XSID27",
|
||||
"signal": "XSID27",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000023"
|
||||
}, {
|
||||
"name": "XSID28",
|
||||
"signal": "XSID28",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000024"
|
||||
}, {
|
||||
"name": "XSID29",
|
||||
"signal": "XSID29",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000025"
|
||||
}, {
|
||||
"name": "XSID30",
|
||||
"signal": "XSID30",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000026"
|
||||
}, {
|
||||
"name": "XSID31",
|
||||
"signal": "XSID31",
|
||||
"non_secure_clients": True,
|
||||
"minor_version": 5,
|
||||
"minor_policy": "RELAXED",
|
||||
"identifier": "0x00000027"
|
||||
}
|
||||
]
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright (c) 2019 ARM Limited
|
||||
#
|
||||
# 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 pytest
|
||||
import os
|
||||
from tools.notifier.mock import MockNotifier
|
||||
from tools.resources import Resources, FileType
|
||||
from tools.psa import find_secure_image
|
||||
|
||||
|
||||
def test_find_secure_image():
|
||||
mock_notifier = MockNotifier()
|
||||
mock_resources = Resources(mock_notifier)
|
||||
ns_image_path = os.path.join('BUILD', 'TARGET_NS', 'app.bin')
|
||||
ns_test_path = os.path.join('BUILD', 'TARGET_NS', 'test.bin')
|
||||
config_s_image_name = 'target_config.bin'
|
||||
default_bin = os.path.join('prebuilt', config_s_image_name)
|
||||
test_bin = os.path.join('prebuilt', 'test.bin')
|
||||
|
||||
with pytest.raises(Exception, match='ns_image_path and configured_s_image_path are mandatory'):
|
||||
find_secure_image(mock_notifier, mock_resources, None, None, FileType.BIN)
|
||||
find_secure_image(mock_notifier, mock_resources, ns_image_path, None, FileType.BIN)
|
||||
find_secure_image(mock_notifier, mock_resources, None, config_s_image_name, FileType.BIN)
|
||||
|
||||
with pytest.raises(Exception, match='image_type must be of type BIN or HEX'):
|
||||
find_secure_image(mock_notifier, mock_resources, ns_image_path, config_s_image_name, None)
|
||||
find_secure_image(mock_notifier, mock_resources, ns_image_path, config_s_image_name, FileType.C_SRC)
|
||||
|
||||
with pytest.raises(Exception, match='No image files found for this target'):
|
||||
find_secure_image(mock_notifier, mock_resources, ns_image_path, config_s_image_name, FileType.BIN)
|
||||
|
||||
dummy_bin = os.path.join('path', 'to', 'dummy.bin')
|
||||
mock_resources.add_file_ref(FileType.BIN, dummy_bin, dummy_bin)
|
||||
|
||||
with pytest.raises(Exception, match='Required secure image not found'):
|
||||
find_secure_image(mock_notifier, mock_resources, ns_image_path, config_s_image_name, FileType.BIN)
|
||||
|
||||
mock_resources.add_file_ref(FileType.BIN, default_bin, default_bin)
|
||||
mock_resources.add_file_ref(FileType.BIN, test_bin, test_bin)
|
||||
secure_image = find_secure_image(mock_notifier, mock_resources, ns_image_path, config_s_image_name, FileType.BIN)
|
||||
assert secure_image == default_bin
|
||||
|
||||
secure_image = find_secure_image(mock_notifier, mock_resources, ns_test_path, config_s_image_name, FileType.BIN)
|
||||
assert secure_image == test_bin
|
|
@ -1,683 +0,0 @@
|
|||
# Copyright (c) 2017-2018 ARM Limited
|
||||
#
|
||||
# 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 filecmp
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import pytest
|
||||
import jsonschema.exceptions as jexcep
|
||||
from jinja2.defaults import DEFAULT_FILTERS
|
||||
from tools.psa.mbed_spm_tfm_common import *
|
||||
from tools.psa.generate_partition_code import *
|
||||
from .test_data import *
|
||||
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def extract_test_name(line):
|
||||
return re.search(r'.*\[(.*)\]', line).group(1)
|
||||
|
||||
|
||||
def dump_manifest_to_json(manifest, test_name, test_dir, create_files=True):
|
||||
"""
|
||||
Create a JSON manifest file from a dictionary.
|
||||
|
||||
:param manifest: The manifest dictionary.
|
||||
:param test_name: Name of the test.
|
||||
:param test_dir: Directory to contain the JSON file.
|
||||
:param create_files: Whether to create the source files listed in the
|
||||
manifest 'source_files' entry.
|
||||
:return: Path of the JSON file.
|
||||
"""
|
||||
test_file_name = test_dir.join('{}.json'.format(test_name))
|
||||
with open(test_file_name.strpath, 'wt') as fh:
|
||||
json.dump(manifest, fh, indent=2)
|
||||
|
||||
# Create all the partition source files
|
||||
if create_files:
|
||||
[test_dir.join(name).write(name) for name in
|
||||
manifest.get('source_files', [])]
|
||||
|
||||
return test_file_name.strpath
|
||||
|
||||
|
||||
def find_priority_key(value):
|
||||
"""
|
||||
Finds the key in 'Manifest.PRIORITY' of a given value.
|
||||
|
||||
:param value: The value.
|
||||
:return: The key of the given value.
|
||||
"""
|
||||
return next(
|
||||
(key for key, val in Manifest.PRIORITY.items() if val == value),
|
||||
None
|
||||
)
|
||||
|
||||
|
||||
def find_permission_key(value):
|
||||
"""
|
||||
Finds the key in 'MmioRegion.MMIO_PERMISIONS' of a given value.
|
||||
|
||||
:param value: The value.
|
||||
:return: The key of the given value.
|
||||
"""
|
||||
return next(
|
||||
(key for key, val in MmioRegion.MMIO_PERMISSIONS.items() if
|
||||
val == value),
|
||||
None
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def temp_test_data(tmpdir_factory):
|
||||
"""
|
||||
Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be
|
||||
used by the tests.
|
||||
This fixture function Creates a valid JSON manifest file in a temporary
|
||||
directory. The scope of this fixture is the entire test session.
|
||||
|
||||
:param tmpdir_factory: Fixture used to create temporary directories.
|
||||
see: https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-factory-fixture
|
||||
:return: A dictionary containing these keys:
|
||||
'dir': The temporary directory object created by this fixture.
|
||||
'json': The created valid manifest JSON file.
|
||||
'manifest': The manifest object read from the JSON file.
|
||||
"""
|
||||
test_dir = tmpdir_factory.mktemp('test_data')
|
||||
fname = dump_manifest_to_json(manifests[0], 'valid_partition', test_dir)
|
||||
valid_manifest = Manifest.from_json(fname)
|
||||
return {'dir': test_dir, 'json': fname, 'manifest': valid_manifest}
|
||||
|
||||
|
||||
"""
|
||||
'modified_json_params' contain the parameters to be used in the
|
||||
'modified_json' fixture.
|
||||
Each key in the dictionary represents a different parameter to be used by
|
||||
'modified_json', so for each test which uses
|
||||
the 'modified_json' fixture, the test will run len(modified_json_params) times,
|
||||
each time with different parameters.
|
||||
Each parameter is a dictionary which contains these keys:
|
||||
'partition': A modified partition dictionary.
|
||||
'assert': The expected assertion which must occur when running with this
|
||||
parameter.
|
||||
"""
|
||||
modified_json_params = {
|
||||
'missing_partition_name': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if k != 'name'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_partition_id': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if k != 'id'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_partition_priority': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k != 'priority'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_entry_point': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k != 'entry_point'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_stack_size': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k != 'stack_size'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_heap_size': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k != 'heap_size'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_source_files': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k != 'source_files'},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_irqs_and_sids': {
|
||||
'partition': {k: manifests[0][k] for k in manifests[0] if
|
||||
k not in ['services', 'irqs']},
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'empty_source_files': {
|
||||
'partition': dict(manifests[0], source_files=[]),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_minor_policy': {
|
||||
'partition': dict(manifests[0],
|
||||
services=invalid_minor_version_policy_rot_srv),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_nspe_callable': {
|
||||
'partition': dict(manifests[0],
|
||||
services=invalid_nspe_callable_rot_srv),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'missing_nspe_callable': {
|
||||
'partition': dict(manifests[0],
|
||||
services=missing_nspe_callable_rot_srv),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_stack_size': {
|
||||
'partition': dict(manifests[0], stack_size='str'),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_heap_size': {
|
||||
'partition': dict(manifests[0], heap_size='str'),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_priority': {
|
||||
'partition': dict(manifests[0], priority='invalid_priority'),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_mmioregion_base': {
|
||||
'partition': dict(manifests[0],
|
||||
mmio_regions=[invalid_mmioregion_base]),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_mmioregion_size': {
|
||||
'partition': dict(manifests[0],
|
||||
mmio_regions=[invalid_mmioregion_size]),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_irq_num': {
|
||||
'partition': dict(manifests[0],
|
||||
irqs=[{"line_num": "str", "signal": "ISR22"}]),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'not_exist_src_filename': {
|
||||
'partition': dict(manifests[0], source_files=['missing.cpp']),
|
||||
'assert': AssertionError
|
||||
},
|
||||
'invalid_partition_id_decimal': {
|
||||
'partition': dict(manifests[0], id=-1),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'invalid_partition_id_hex': {
|
||||
'partition': dict(manifests[0], id='0xFFFFFFFF'),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'duplicates_extern_sids': {
|
||||
'partition': dict(manifests[0], extern_sids=['SID66', 'SID66']),
|
||||
'assert': jexcep.ValidationError
|
||||
},
|
||||
'exceeding_services': {
|
||||
'partition': dict(manifests[1], services=exceeding_services),
|
||||
'assert': AssertionError
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(params=modified_json_params.values(),
|
||||
ids=modified_json_params.keys())
|
||||
def modified_json(request, temp_test_data):
|
||||
"""
|
||||
Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be
|
||||
used by the tests.
|
||||
This fixture function Creates a JSON manifest file from a given partition
|
||||
dictionary and save it
|
||||
to a temporary directory.
|
||||
This fixture uses the 'temp_test_data' fixture.
|
||||
This fixture is a parametrized fixture
|
||||
(https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures).
|
||||
The scope of this fixture is a specific test.
|
||||
|
||||
:param request: Request object which contain the current parameter from
|
||||
'modified_json_params'.
|
||||
:param temp_test_data: The 'temp_test_data' fixture.
|
||||
:return: A list containing these values:
|
||||
- The created manifest JSON file for the current parameter.
|
||||
- The expected assertion for the current parameter.
|
||||
"""
|
||||
testname = extract_test_name(request.node.name)
|
||||
test_file = dump_manifest_to_json(request.param['partition'], testname,
|
||||
temp_test_data['dir'], False)
|
||||
return test_file, request.param['assert']
|
||||
|
||||
|
||||
def test_invalid_json(modified_json):
|
||||
"""
|
||||
Test which gets an invalid JSON manifest file (from the
|
||||
'modified_json' fixture) and tries to create a
|
||||
Manifest object from it.
|
||||
The test expects an assertion to happen.
|
||||
|
||||
:param modified_json: The 'modified_json' fixture.
|
||||
:return:
|
||||
"""
|
||||
with pytest.raises(modified_json[1]):
|
||||
Manifest.from_json(modified_json[0])
|
||||
|
||||
|
||||
def test_valid_json(temp_test_data):
|
||||
"""
|
||||
Test which gets a valid JSON manifest file (from the 'temp_test_data'
|
||||
fixture) and tries to create a Manifest object from it.
|
||||
The test expects the Manifest to be same as the Manifest created by the
|
||||
'temp_test_data' fixture.
|
||||
|
||||
:param temp_test_data: The 'temp_test_data' fixture.
|
||||
:return:
|
||||
"""
|
||||
manifest = Manifest.from_json(temp_test_data['json'])
|
||||
assert manifest == temp_test_data['manifest']
|
||||
|
||||
|
||||
# Test parametrization decorator
|
||||
# See https://docs.pytest.org/en/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions
|
||||
# Contain the parameters to be used in the 'test_validate_partition_manifest'
|
||||
# test. It defines a list of (manifest, assertion) tuples which each entry
|
||||
# will be the input of the 'test_validate_partition_manifest' test, the test
|
||||
# will run len(LIST_OF_TUPPLES) times, each time with different (manifest,
|
||||
# assertion) tuple.
|
||||
# The tuple fields are:
|
||||
# 'manifest': A modified partition dictionary.
|
||||
# 'assertion': A tuple containing the expected assertion and assertion
|
||||
# string which must occur when running with this parameter.
|
||||
@pytest.mark.parametrize(
|
||||
'manifests, assertion',
|
||||
[
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], name=manifests[0]['name'])],
|
||||
(ValueError, r'Partition name .* is not unique, .*'),
|
||||
id='duplicate_partition_name'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], id=manifests[0]['id'])],
|
||||
(ValueError, r'Partition id .* is not unique, .*'),
|
||||
id='duplicate_partition_id'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], services=manifests[0]['services'])],
|
||||
(ValueError, r'Root of Trust Service name .* is found in both .*'),
|
||||
id='duplicate_rot_srv_name'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], services=duplicate_signal_rot_services)],
|
||||
(ValueError, r'Root of Trust Service signal .* is found in both .*'),
|
||||
id='duplicate_rot_srv_signal'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], services=duplicate_identifier_rot_services)],
|
||||
(ValueError, r'Root of Trust Service identifier .* is found in both .*'),
|
||||
id='duplicate_rot_srv_identifier'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], irqs=duplicate_signal_irqs)],
|
||||
(ValueError, r'IRQ signal .* is found in both .*'),
|
||||
id='duplicate_irq_signal'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], irqs=duplicate_line_num_irqs)],
|
||||
(ValueError, r'IRQ line number .* is found in both .*'),
|
||||
id='duplicate_irq_line_num'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], extern_sids=['SID66', 'SID999'])],
|
||||
(ValueError, r'External SID\(s\) .* can\'t be found in any partition manifest.'),
|
||||
id='orphan_extern_ids'
|
||||
),
|
||||
pytest.param(
|
||||
[manifests[0], dict(manifests[1], extern_sids=[manifests[0]['services'][0]['name']])],
|
||||
(ValueError, r'Detected a circular call dependency between the partitions.'),
|
||||
id='circular_call_dependency'
|
||||
),
|
||||
pytest.param(
|
||||
[{k: manifests[0][k] for k in manifests[0] if k != 'extern_sids'},
|
||||
dict({k: manifests[1][k] for k in manifests[1] if k != 'services'
|
||||
and k != 'irqs'}, services=spe_contained_rot_services)],
|
||||
(ValueError, r'Partition .* is not accessible from NSPE '
|
||||
'and not referenced by any other partition.'),
|
||||
id='dead_partition'
|
||||
)
|
||||
]
|
||||
)
|
||||
def test_validate_partition_manifest(request, temp_test_data, manifests, assertion):
|
||||
"""
|
||||
Test which creates an invalid manifest object (after passing JSON schema
|
||||
validation) and call
|
||||
validate_partition_manifests() with it and with a valid manifest object.
|
||||
The test expects an assertion to happen.
|
||||
|
||||
:param request: Request object.
|
||||
:param temp_test_data: The 'temp_test_data' fixture.
|
||||
:param manifest: The manifest value from the (manifest, assertion) tuple
|
||||
for the current parameter.
|
||||
:param assertion: The assertion value from the (manifest, assertion) tuple
|
||||
for the current parameter.
|
||||
:return:
|
||||
"""
|
||||
test_name = extract_test_name(request.node.name)
|
||||
jsons = [dump_manifest_to_json(m, '%s_%d' % (test_name, i), temp_test_data['dir']) for i, m in enumerate(manifests)]
|
||||
created_manifests, _ = parse_manifests(jsons)
|
||||
|
||||
with pytest.raises(assertion[0], match=assertion[1]):
|
||||
validate_partition_manifests(created_manifests)
|
||||
|
||||
|
||||
"""
|
||||
'verify_json_params' contain the parameters to be used in the 'verify_json'
|
||||
fixture. Each key in the dictionary represents a different parameter to be used
|
||||
by 'verify_json', so for each test which uses the 'verify_json' fixture, the
|
||||
test will run len(verify_json_params) times, each time with different
|
||||
parameters.
|
||||
Each parameter is a dictionary which contains these keys:
|
||||
'partition': A modified partition dictionary.
|
||||
'field': The modified field name.
|
||||
'expected': The expected field object.
|
||||
"""
|
||||
verify_json_params = {
|
||||
'missing_minor_version_rot_services': {
|
||||
'partition': dict(manifests[0],
|
||||
services=missing_minor_version_rot_srv),
|
||||
'field': 'rot_services',
|
||||
'expected': [
|
||||
RotService(
|
||||
name='SID1', identifier='0x00000001',signal='SID1',
|
||||
minor_policy='RELAXED', non_secure_clients=True, minor_version=1
|
||||
)
|
||||
]
|
||||
},
|
||||
'missing_minor_version_policy_rot_services': {
|
||||
'partition': dict(manifests[0],
|
||||
services=missing_minor_version_policy_rot_srv),
|
||||
'field': 'rot_services',
|
||||
'expected': [
|
||||
RotService(
|
||||
name='SID2', identifier='0x00000002', signal='SID2',
|
||||
minor_policy='STRICT', non_secure_clients=True, minor_version=1
|
||||
)
|
||||
]
|
||||
},
|
||||
'missing_minor_completley_rot_services': {
|
||||
'partition': dict(manifests[0],
|
||||
services=missing_minor_completley_rot_srv),
|
||||
'field': 'rot_services',
|
||||
'expected': [
|
||||
RotService(
|
||||
name='SID2', identifier='0x00000002', signal='SID2',
|
||||
minor_policy='STRICT', non_secure_clients=True, minor_version=1
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(params=verify_json_params.values(),
|
||||
ids=verify_json_params.keys())
|
||||
def verify_json(request, tmpdir_factory):
|
||||
"""
|
||||
Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be
|
||||
used by the tests.
|
||||
This fixture function Creates 2 JSON manifest files (The 1st from
|
||||
'verify_json_params', the 2nd from manifests[1]) and saves them to a
|
||||
temporary directory. This fixture is a parametrized fixture
|
||||
(https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures).
|
||||
The scope of this fixture is a specific test.
|
||||
|
||||
:param request: Request object which contain the current parameter from
|
||||
'verify_json_params'.
|
||||
:param tmpdir_factory: The 'tmpdir_factory' fixture.
|
||||
:return: A dictionary containing these keys:
|
||||
'files_list': A list of the created manifest JSON files.
|
||||
'field': The changed field in the 1st manifest.
|
||||
'expected': The expected 'field' object.
|
||||
"""
|
||||
test_dir = tmpdir_factory.mktemp('test_data')
|
||||
test_name = extract_test_name(request.node.name)
|
||||
files_list = [
|
||||
dump_manifest_to_json(request.param['partition'], '%s1' % test_name,
|
||||
test_dir),
|
||||
dump_manifest_to_json(dict(manifests[1], extern_sids=[]),
|
||||
'%s2' % test_name, test_dir)
|
||||
]
|
||||
return {'files_list': files_list, 'field': request.param['field'],
|
||||
'expected': request.param['expected']}
|
||||
|
||||
|
||||
def test_verify_json(verify_json):
|
||||
"""
|
||||
Test which gets 2 JSON manifest files (from the 'verify_json' fixture),
|
||||
create Manifest objects from them, call validate_partition_manifests() on
|
||||
the manifest objects and check that the 1st Manifest object is as expected.
|
||||
|
||||
:param verify_json: The 'verify_json' fixture.
|
||||
:return:
|
||||
"""
|
||||
test_manifests, _ = parse_manifests(verify_json['files_list'])
|
||||
validate_partition_manifests(test_manifests)
|
||||
assert getattr(test_manifests[0], verify_json['field']) == verify_json['expected']
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def test_template_setup(tmpdir_factory):
|
||||
"""
|
||||
Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be
|
||||
used by the tests. This fixture function Creates JSON manifest files,
|
||||
Manifest objects from 'manifest' and template files in a temporary
|
||||
directory. The scope of this fixture is the entire test session.
|
||||
|
||||
:param tmpdir_factory: Fixture used to create temporary directories.
|
||||
see: https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-factory-fixture
|
||||
:return: A dictionary containing these keys:
|
||||
'dir': The temporary directory object created by this fixture.
|
||||
'template_files': List of the created template files.
|
||||
'manifest_files': List of the created manifest JSON files.
|
||||
'manifests': List of the created Manifest objects.
|
||||
'filters': Dictionary with additional filters for
|
||||
generate_source_files()
|
||||
"""
|
||||
|
||||
def find_priority_key(value):
|
||||
"""
|
||||
Finds the key in 'Manifest.PRIORITY' of a given value.
|
||||
|
||||
:param value: The value.
|
||||
:return: The key of the given value.
|
||||
"""
|
||||
return next(
|
||||
(key for key, val in Manifest.PRIORITY.items() if val == value),
|
||||
None)
|
||||
|
||||
def find_permission_key(value):
|
||||
"""
|
||||
Finds the key in 'MmioRegion.MMIO_PERMISIONS' of a given value.
|
||||
|
||||
:param value: The value.
|
||||
:return: The key of the given value.
|
||||
"""
|
||||
return next((key for key, val in MmioRegion.MMIO_PERMISSIONS.items() if
|
||||
val == value), None)
|
||||
|
||||
test_dir = tmpdir_factory.mktemp('test_data')
|
||||
manifest_files = [
|
||||
dump_manifest_to_json(manifest, manifest['name'], test_dir) for
|
||||
manifest in manifests]
|
||||
manifest_objects, regions = parse_manifests(manifest_files)
|
||||
filters = {
|
||||
'basename': os.path.basename,
|
||||
'find_priority_key': find_priority_key,
|
||||
'find_permission_key': find_permission_key
|
||||
}
|
||||
template_files = [test_dir.join('_NAME_data.json.tpl'),
|
||||
test_dir.join('common.json.tpl')]
|
||||
for template, _file in [(test_partition_template, template_files[0]),
|
||||
(test_common_template, template_files[1])]:
|
||||
_file.write(template)
|
||||
template_files = [_file.strpath for _file in template_files]
|
||||
|
||||
expected_common_files = [test_dir.join('common.json')]
|
||||
for output, _file in [(test_common_expected, expected_common_files[0])]:
|
||||
_file.write(output)
|
||||
expected_common_files = [_file.strpath for _file in expected_common_files]
|
||||
|
||||
return {
|
||||
'dir': test_dir.strpath,
|
||||
'template_files': template_files,
|
||||
'manifest_files': manifest_files,
|
||||
'common_files': expected_common_files,
|
||||
'manifests': manifest_objects,
|
||||
'region_list': regions,
|
||||
'filters': filters
|
||||
}
|
||||
|
||||
|
||||
def test_generate_source_files(test_template_setup):
|
||||
"""
|
||||
Test which calls generate_source_files() with the data from
|
||||
'test_template_setup' fixture and checks normal output.
|
||||
|
||||
:param test_template_setup: The 'test_template_setup' fixture.
|
||||
:return:
|
||||
"""
|
||||
|
||||
before_file_list = set(os.listdir(test_template_setup['dir']))
|
||||
partition_templates = filter(lambda filename: '_NAME_' in filename, test_template_setup['template_files'])
|
||||
common_templates = filter(lambda filename: '_NAME_' not in filename, test_template_setup['template_files'])
|
||||
common_templates = {
|
||||
t: path_join(test_template_setup['dir'], os.path.basename(os.path.splitext(t)[0])) for t in common_templates
|
||||
}
|
||||
|
||||
region_pair_list = list(itertools.combinations(test_template_setup['region_list'], 2))
|
||||
for manifest in test_template_setup['manifests']:
|
||||
generate_source_files(
|
||||
templates=manifest.templates_to_files(partition_templates, test_template_setup['dir'], test_template_setup['dir']),
|
||||
render_args={
|
||||
'partition': manifest,
|
||||
'dependent_partitions': manifest.find_dependencies(test_template_setup['manifests'])
|
||||
},
|
||||
extra_filters=test_template_setup['filters']
|
||||
)
|
||||
|
||||
generate_source_files(
|
||||
common_templates,
|
||||
render_args={
|
||||
'partitions': test_template_setup['manifests'],
|
||||
'region_pair_list': region_pair_list
|
||||
},
|
||||
extra_filters=test_template_setup['filters']
|
||||
)
|
||||
|
||||
after_file_list = set(os.listdir(test_template_setup['dir']))
|
||||
generated_files = list(after_file_list.difference(before_file_list))
|
||||
|
||||
for gen_file in [os.path.join(test_template_setup['dir'], f) for f in generated_files]:
|
||||
"""
|
||||
For each generated json file in 'autogen_dir':
|
||||
1. Load the json file to a dictionary named 'generated'.
|
||||
2. If it was generated from a partition template ('generated' has a 'name' key):
|
||||
a) Read the original manifest json from the test temp dir.
|
||||
b) Load the manifest json file to a dictionary named 'expected'.
|
||||
Else (generated from a common template):
|
||||
a) Calculate 'region_list'.
|
||||
b) Build the 'expected' dictionary with values from the original manifest objects.
|
||||
3. Compare 'generated' with 'expected'.
|
||||
"""
|
||||
with open(gen_file) as fh:
|
||||
generated = json.load(fh)
|
||||
|
||||
if 'name' in generated:
|
||||
input_file = os.path.join(test_template_setup['dir'],
|
||||
generated['name'] + '.json')
|
||||
assert os.path.isfile(input_file)
|
||||
assert input_file in test_template_setup['manifest_files']
|
||||
with open(input_file) as fh:
|
||||
expected = json.load(fh)
|
||||
else:
|
||||
expected = {
|
||||
'num_of_partitions': len(test_template_setup['manifests']),
|
||||
'partition_names': [manifest.name for manifest in
|
||||
test_template_setup['manifests']],
|
||||
'num_of_region_pairs': len(region_pair_list)
|
||||
}
|
||||
assert generated == expected
|
||||
|
||||
|
||||
circular_call_dependency_params = {
|
||||
'no manifests': {
|
||||
'manifests': [],
|
||||
'result': False
|
||||
},
|
||||
'one manifest': {
|
||||
'manifests': ['PARTITION1'],
|
||||
'result': False
|
||||
},
|
||||
'2 manifests with dependency': {
|
||||
'manifests': ['PARTITION1', 'PARTITION2'],
|
||||
'result': True
|
||||
},
|
||||
'2 manifests without dependency': {
|
||||
'manifests': ['PARTITION1', 'PARTITION3'],
|
||||
'result': False
|
||||
},
|
||||
'5 manifests with dependency': {
|
||||
'manifests': ['PARTITION1', 'PARTITION3', 'PARTITION4', 'PARTITION5', 'PARTITION6'],
|
||||
'result': True
|
||||
},
|
||||
'5 manifests without dependency': {
|
||||
'manifests': ['PARTITION1', 'PARTITION3', 'PARTITION4', 'PARTITION6', 'PARTITION7'],
|
||||
'result': False
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(params=circular_call_dependency_params.values(),
|
||||
ids=circular_call_dependency_params.keys())
|
||||
def circular_dependencies(request, tmpdir_factory):
|
||||
"""
|
||||
Fixture (https://docs.pytest.org/en/latest/fixture.html) function to be
|
||||
used by the tests.
|
||||
This fixture function Creates a JSON manifest file from a given partition
|
||||
dictionary and save it
|
||||
to a temporary directory.
|
||||
This fixture uses the 'temp_test_data' fixture.
|
||||
This fixture is a parametrized fixture
|
||||
(https://docs.pytest.org/en/latest/fixture.html#parametrizing-fixtures).
|
||||
The scope of this fixture is a specific test.
|
||||
|
||||
:param request: Request object which contain the current parameter from
|
||||
'circular_call_dependency_params'.
|
||||
:param temp_test_data: The 'temp_test_data' fixture.
|
||||
:return: A Dictionary containing these values:
|
||||
- files - list of manifest filesgenerated
|
||||
- The expected result from check_circular_call_dependencies
|
||||
"""
|
||||
test_dir = tmpdir_factory.mktemp('test_data')
|
||||
|
||||
test_manifests = filter(lambda x: x['name'] in request.param['manifests'],
|
||||
manifests_for_circular_call_dependency_checks)
|
||||
manifest_files = [
|
||||
dump_manifest_to_json(manifest, manifest['name'], test_dir) for
|
||||
manifest in test_manifests]
|
||||
|
||||
return {'files': manifest_files, 'result': request.param['result']}
|
||||
|
||||
|
||||
def test_check_circular_call_dependencies(circular_dependencies):
|
||||
"""
|
||||
Test detection of circular call dependencies between the partitions.
|
||||
The test performs the circular call dependency check in a few
|
||||
predefined partition topologies and compares the result with the expected value.
|
||||
|
||||
:param circular_dependencies: the 'circular_dependencies' fixture
|
||||
:return:
|
||||
"""
|
||||
objects, _ = parse_manifests(circular_dependencies['files'])
|
||||
assert check_circular_call_dependencies(objects) == circular_dependencies[
|
||||
'result']
|
Loading…
Reference in New Issue