mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-08 05:43:53 +00:00
feat(auth): standalone CoT dt2c tool
Add the standalone CoT dt2c tool for CoT DTB conversion to c file Change-Id: If28e580a4c2825f5dc9008e93cd2aae3fc173e73 Signed-off-by: Xialin Liu <Xialin.Liu@ARM.com>
This commit is contained in:
parent
3146a70af2
commit
4274d6f885
31 changed files with 4143 additions and 0 deletions
68
tools/cot_dt2c/Makefile
Normal file
68
tools/cot_dt2c/Makefile
Normal file
|
@ -0,0 +1,68 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
##* Variables
|
||||
SHELL := /usr/bin/env bash
|
||||
PYTHON := python
|
||||
PYTHONPATH := `pwd`
|
||||
|
||||
.PHONY: dist
|
||||
dist: clean
|
||||
poetry build
|
||||
|
||||
#* Installation
|
||||
.PHONY: dev-install
|
||||
dev-install:
|
||||
pip3 install mypy
|
||||
pip3 install pytest
|
||||
pip install -r requirements.txt
|
||||
poetry lock -n && poetry export --without-hashes > requirements.txt
|
||||
poetry install -n
|
||||
-poetry run mypy --install-types --non-interactive ./
|
||||
|
||||
.PHONY: install
|
||||
install: dist
|
||||
pip install mypy
|
||||
pip install pytest
|
||||
pip install -r requirements.txt
|
||||
pip install dist/*.whl
|
||||
|
||||
clean-test: ## remove test and coverage artifacts
|
||||
rm -fr .tox/
|
||||
rm -f .coverage
|
||||
rm -fr htmlcov/
|
||||
|
||||
clean-pyc: ## remove Python file artifacts
|
||||
find . -name '*.pyc' -exec rm -f {} +
|
||||
find . -name '*.pyo' -exec rm -f {} +
|
||||
find . -name '*~' -exec rm -f {} +
|
||||
find . -name '__pycache__' -exec rm -fr {} +
|
||||
find . | grep -E ".pytest_cache" | xargs rm -rf
|
||||
find . | grep -E ".mypy_cache" | xargs rm -rf
|
||||
|
||||
|
||||
clean-build: ## remove build artifacts
|
||||
rm -fr build/
|
||||
rm -fr dist/
|
||||
rm -fr .eggs/
|
||||
find . -name '*.egg-info' -exec rm -fr {} +
|
||||
find . -name '*.egg' -exec rm -f {} +
|
||||
|
||||
clean-tmp:
|
||||
rm -rf ./tmp
|
||||
|
||||
#* Cleaning
|
||||
.PHONY: clean clean-build clean-pyc clean-test
|
||||
clean: uninstall clean-build clean-pyc clean-test clean-tmp ## remove all build, test, coverage and Python artifacts
|
||||
|
||||
uninstall:
|
||||
pip uninstall -y cot-dt2c
|
||||
|
||||
.PHONY: reinstall
|
||||
reinstall: clean install
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
PYTHONPATH=$(PYTHONPATH) poetry run pytest -c pyproject.toml tests/
|
202
tools/cot_dt2c/cot_dt2c/LICENSE
Normal file
202
tools/cot_dt2c/cot_dt2c/LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
25
tools/cot_dt2c/cot_dt2c/__init__.py
Normal file
25
tools/cot_dt2c/cot_dt2c/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# type: ignore[attr-defined]
|
||||
|
||||
#
|
||||
# Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from importlib import metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
try:
|
||||
return importlib_metadata.version(__name__)
|
||||
except importlib_metadata.PackageNotFoundError: # pragma: no cover
|
||||
return "unknown"
|
||||
|
||||
|
||||
version: str = get_version()
|
10
tools/cot_dt2c/cot_dt2c/__main__.py
Normal file
10
tools/cot_dt2c/cot_dt2c/__main__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
# type: ignore[attr-defined]
|
||||
#
|
||||
# Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
from cot_dt2c.cli import cli
|
||||
if __name__ == "__main__":
|
||||
cli()
|
39
tools/cot_dt2c/cot_dt2c/cli.py
Normal file
39
tools/cot_dt2c/cot_dt2c/cli.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
from pathlib import Path
|
||||
from cot_dt2c.cot_dt2c import generateMain
|
||||
from cot_dt2c.cot_dt2c import validateMain
|
||||
from cot_dt2c.cot_dt2c import visualizeMain
|
||||
from cot_dt2c.dt_validator import dtValidatorMain
|
||||
|
||||
import click
|
||||
|
||||
@click.group()
|
||||
@click.version_option()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
@cli.command()
|
||||
@click.argument("inputfile", type=click.Path(dir_okay=True))
|
||||
@click.argument("outputfile", type=click.Path(dir_okay=True))
|
||||
def convert_to_c(inputfile, outputfile):
|
||||
generateMain(inputfile, outputfile)
|
||||
|
||||
@cli.command()
|
||||
@click.argument("inputfile", type=click.Path(dir_okay=True))
|
||||
def validate_cot(inputfile):
|
||||
validateMain(inputfile)
|
||||
|
||||
@cli.command()
|
||||
@click.argument("inputfile", type=click.Path(dir_okay=True))
|
||||
def visualize_cot(inputfile):
|
||||
visualizeMain(inputfile)
|
||||
|
||||
@cli.command()
|
||||
@click.argument("inputfiledir", type=click.Path(dir_okay=True))
|
||||
def validate_dt(inputfiledir):
|
||||
dtValidatorMain(inputfiledir)
|
30
tools/cot_dt2c/cot_dt2c/cot_dt2c.py
Normal file
30
tools/cot_dt2c/cot_dt2c/cot_dt2c.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import sys
|
||||
from cot_dt2c.cot_parser import COT
|
||||
|
||||
def generateMain(input, output=None):
|
||||
cot = COT(input, output)
|
||||
cot.generate_c_file()
|
||||
|
||||
def validateMain(input):
|
||||
cot = COT(input)
|
||||
if not cot.validate_nodes():
|
||||
print("not a valid CoT DT file")
|
||||
|
||||
def visualizeMain(input):
|
||||
cot = COT(input)
|
||||
cot.tree_visualization()
|
||||
|
||||
if __name__=="__main__":
|
||||
if (len(sys.argv) < 2):
|
||||
print("usage: python3 " + sys.argv[0] + " [dtsi file path] [optional output c file path]")
|
||||
exit()
|
||||
if len(sys.argv) == 3:
|
||||
generateMain(sys.argv[1], sys.argv[2])
|
||||
if len(sys.argv) == 2:
|
||||
validateMain(sys.argv[1])
|
804
tools/cot_dt2c/cot_dt2c/cot_parser.py
Normal file
804
tools/cot_dt2c/cot_dt2c/cot_parser.py
Normal file
|
@ -0,0 +1,804 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import sys
|
||||
import re
|
||||
from cot_dt2c.pydevicetree.source.parser import ifdef_stack
|
||||
from cot_dt2c.pydevicetree.ast import CellArray, LabelReference
|
||||
from cot_dt2c.pydevicetree import *
|
||||
from pathlib import Path
|
||||
|
||||
def extractNumber(s):
|
||||
for i in s:
|
||||
if i.isdigit():
|
||||
return (int)(i)
|
||||
|
||||
return -1
|
||||
|
||||
def removeNumber(s):
|
||||
result = ''.join([i for i in s if not i.isdigit()])
|
||||
return result
|
||||
|
||||
class COT:
|
||||
def __init__(self, inputfile: str, outputfile=None):
|
||||
with open(inputfile, 'r') as f:
|
||||
contents = f.read()
|
||||
pos = contents.find("cot")
|
||||
if pos == -1:
|
||||
print("not a valid CoT DT file")
|
||||
exit(1)
|
||||
|
||||
contents = contents[pos:]
|
||||
|
||||
try:
|
||||
self.tree = Devicetree.parseStr(contents)
|
||||
except:
|
||||
print("not a valid CoT DT file")
|
||||
exit(1)
|
||||
|
||||
self.output = outputfile
|
||||
self.input = inputfile
|
||||
self.has_root = False
|
||||
|
||||
# edge cases
|
||||
certs = self.get_all_certificates()
|
||||
for c in certs:
|
||||
if self.if_root(c):
|
||||
if not c.get_fields("signing-key"):
|
||||
c.properties.append(Property("signing-key", CellArray([LabelReference("subject_pk")])))
|
||||
|
||||
def print_cert_info(self, node:Node):
|
||||
img_id = node.get_field("image-id").values[0].replace('"', "")
|
||||
sign_key = self.get_sign_key(node)
|
||||
nv = self.get_nv_ctr(node)
|
||||
|
||||
info = "<b>name:</b> {}<br><b>image-id:</b> {}<br>{}{}{}"\
|
||||
.format(node.name, img_id, "<b>root-certificate</b><br>" if self.if_root(node) else "", \
|
||||
"<b>signing-key:</b> " + self.extract_label(sign_key) + "<br>" if sign_key else "", \
|
||||
"<b>nv counter:</b> " + self.extract_label(nv) + "<br>" if nv else "")
|
||||
return info
|
||||
|
||||
def print_data_info(self, node:Node):
|
||||
oid = node.get_field("oid")
|
||||
info = "<b>name:</b> {}<br><b>oid:</b> {}<br>" \
|
||||
.format(node.name, oid)
|
||||
|
||||
return info
|
||||
|
||||
def print_img_info(self, node:Node):
|
||||
hash = self.extract_label(node.get_fields("hash"))
|
||||
img_id = node.get_field("image-id").values[0].replace('"', "")
|
||||
info = "<b>name:</b> {}<br><b>image-id:</b> {}<br><b>hash:</b> {}"\
|
||||
.format(node.name, img_id, hash)
|
||||
|
||||
return info
|
||||
|
||||
def tree_width(self, parent_set, root):
|
||||
ans = 1
|
||||
stack = [root]
|
||||
|
||||
while stack:
|
||||
tmp_stack = []
|
||||
while stack:
|
||||
cur_node = stack.pop()
|
||||
child = parent_set[cur_node]
|
||||
for c in child:
|
||||
tmp_stack.append(c)
|
||||
|
||||
stack = tmp_stack.copy()
|
||||
ans = max(ans, len(tmp_stack))
|
||||
|
||||
return ans
|
||||
|
||||
def resolve_lay(self, parent_set, lay, name_idx, root, bounds, break_name):
|
||||
child = parent_set[root]
|
||||
|
||||
if len(child) == 0:
|
||||
return
|
||||
|
||||
width = []
|
||||
total_width = 0
|
||||
for c in child:
|
||||
w = self.tree_width(parent_set, c)
|
||||
width.append(w)
|
||||
total_width += w
|
||||
|
||||
allow_width = bounds[1] - bounds[0]
|
||||
interval = allow_width / total_width
|
||||
start = bounds[0]
|
||||
for i, c in enumerate(child):
|
||||
end = start + interval * width[i]
|
||||
new_bounds = [start, end]
|
||||
lay[name_idx[c]][0] = start + (end - start) / 2
|
||||
if end - start < 0.28:
|
||||
break_name.add(c)
|
||||
start = end
|
||||
self.resolve_lay(parent_set, lay, name_idx, c, new_bounds, break_name)
|
||||
|
||||
def tree_visualization(self):
|
||||
import igraph
|
||||
from igraph import Graph, EdgeSeq
|
||||
import collections
|
||||
|
||||
cert = self.get_certificates()
|
||||
pk = self.get_rot_keys()
|
||||
nv = self.get_nv_counters()
|
||||
image = self.get_images()
|
||||
|
||||
certs = cert.children
|
||||
if pk:
|
||||
pks = pk.children
|
||||
else:
|
||||
pks = []
|
||||
nvs = nv.children
|
||||
images = image.children
|
||||
|
||||
root_name = "CoT"
|
||||
|
||||
G = Graph()
|
||||
detail = []
|
||||
lay = []
|
||||
name_idx = {}
|
||||
parent_set = collections.defaultdict(list)
|
||||
|
||||
G.add_vertex(root_name)
|
||||
detail.append("CoT Root")
|
||||
name_idx[root_name] = len(lay)
|
||||
lay.append([0,0])
|
||||
|
||||
G.add_vertex(cert.name)
|
||||
G.add_edge(root_name, cert.name)
|
||||
detail.append("All Certificates")
|
||||
name_idx[cert.name] = len(lay)
|
||||
lay.append([0, 1])
|
||||
parent_set[root_name].append(cert.name)
|
||||
|
||||
if pk:
|
||||
G.add_vertex(pk.name)
|
||||
detail.append("All Public Trusted Key")
|
||||
G.add_edge(root_name, pk.name)
|
||||
name_idx[pk.name] = len(lay)
|
||||
lay.append([-2.0, 1])
|
||||
parent_set[root_name].append(pk.name)
|
||||
|
||||
G.add_vertex(nv.name)
|
||||
detail.append("All NV Counters")
|
||||
G.add_edge(root_name, nv.name)
|
||||
name_idx[nv.name] = len(lay)
|
||||
lay.append([2.0, 1])
|
||||
parent_set[root_name].append(nv.name)
|
||||
|
||||
if pks:
|
||||
for i, p in enumerate(pks):
|
||||
G.add_vertex(p.name)
|
||||
detail.append(self.print_data_info(p))
|
||||
G.add_edge(pk.name, p.name)
|
||||
name_idx[p.name] = len(lay)
|
||||
parent_set[pk.name].append(p.name)
|
||||
lay.append([0, lay[name_idx[pk.name]][1] + 1])
|
||||
|
||||
for c in certs:
|
||||
G.add_vertex(c.name)
|
||||
detail.append(self.print_cert_info(c))
|
||||
name_idx[c.name] = len(lay)
|
||||
if self.if_root(c):
|
||||
G.add_edge(cert.name, c.name)
|
||||
parent_set[cert.name].append(c.name)
|
||||
lay.append([0, 2])
|
||||
else:
|
||||
parent = self.extract_label(c.get_fields("parent"))
|
||||
G.add_edge(parent, c.name)
|
||||
parent_set[parent].append(c.name)
|
||||
lay.append([0, lay[name_idx[parent]][1] + 1])
|
||||
|
||||
for idx, i in enumerate(images):
|
||||
G.add_vertex(i.name)
|
||||
detail.append(self.print_img_info(i))
|
||||
parent = self.extract_label(i.get_fields("parent"))
|
||||
G.add_edge(parent, i.name)
|
||||
parent_set[parent].append(i.name)
|
||||
name_idx[i.name] = len(lay)
|
||||
lay.append([0, lay[name_idx[parent]][1] + 1])
|
||||
|
||||
for i, n in enumerate(nvs):
|
||||
G.add_vertex(n.name)
|
||||
detail.append(self.print_data_info(n))
|
||||
G.add_edge(nv.name, n.name)
|
||||
name_idx[n.name] = len(lay)
|
||||
parent_set[nv.name].append(n.name)
|
||||
lay.append([0, lay[name_idx[nv.name]][1] + 1])
|
||||
|
||||
break_name = set()
|
||||
self.resolve_lay(parent_set, lay, name_idx, root_name, [-3, 3], break_name)
|
||||
#lay = G.layout('rt')
|
||||
|
||||
numVertex = len(G.get_vertex_dataframe())
|
||||
vertices = G.get_vertex_dataframe()
|
||||
v_label = []
|
||||
|
||||
for i in vertices['name']:
|
||||
if i in break_name and len(i) > 10:
|
||||
middle = len(i) // 2
|
||||
v_label.append(i[:middle] + "<br>" + i[middle:])
|
||||
else:
|
||||
v_label.append(i)
|
||||
|
||||
position = {k: lay[k] for k in range(numVertex)}
|
||||
Y = [lay[k][1] for k in range(numVertex)]
|
||||
M = max(Y)
|
||||
|
||||
es = EdgeSeq(G) # sequence of edges
|
||||
E = [e.tuple for e in G.es] # list of edges
|
||||
|
||||
L = len(position)
|
||||
Xn = [position[k][0] for k in range(L)]
|
||||
Yn = [2*M-position[k][1] for k in range(L)]
|
||||
Xe = []
|
||||
Ye = []
|
||||
for edge in E:
|
||||
Xe += [position[edge[0]][0], position[edge[1]][0], None]
|
||||
Ye += [2*M-position[edge[0]][1], 2*M-position[edge[1]][1], None]
|
||||
|
||||
labels = v_label
|
||||
|
||||
import plotly.graph_objects as go
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatter(x = Xe,
|
||||
y = Ye,
|
||||
mode = 'lines',
|
||||
line = dict(color='rgb(210,210,210)', width=2),
|
||||
hoverinfo = 'none'
|
||||
))
|
||||
fig.add_trace(go.Scatter(x = Xn,
|
||||
y = Yn,
|
||||
mode = 'markers',
|
||||
name = 'detail',
|
||||
marker = dict(symbol = 'circle-dot',
|
||||
size = 50,
|
||||
color = 'rgba(135, 206, 250, 0.8)', #'#DB4551',
|
||||
line = dict(color='MediumPurple', width=3)
|
||||
),
|
||||
text=detail,
|
||||
hoverinfo='text',
|
||||
hovertemplate =
|
||||
'<b>Detail</b><br>'
|
||||
'%{text}',
|
||||
opacity=0.8
|
||||
))
|
||||
|
||||
def make_annotations(pos, text, font_size=10, font_color='rgb(0,0,0)'):
|
||||
L = len(pos)
|
||||
if len(text) != L:
|
||||
raise ValueError('The lists pos and text must have the same len')
|
||||
annotations = []
|
||||
for k in range(L):
|
||||
annotations.append(
|
||||
dict(
|
||||
text = labels[k],
|
||||
x = pos[k][0], y = 2*M-position[k][1],
|
||||
xref = 'x1', yref = 'y1',
|
||||
font = dict(color = font_color, size = font_size),
|
||||
showarrow = False)
|
||||
)
|
||||
return annotations
|
||||
|
||||
axis = dict(showline=False, # hide axis line, grid, ticklabels and title
|
||||
zeroline=False,
|
||||
showgrid=False,
|
||||
showticklabels=False,
|
||||
)
|
||||
|
||||
fig.update_layout(title= 'CoT Device Tree',
|
||||
annotations=make_annotations(position, v_label),
|
||||
font_size=12,
|
||||
showlegend=False,
|
||||
xaxis=axis,
|
||||
yaxis=axis,
|
||||
margin=dict(l=40, r=40, b=85, t=100),
|
||||
hovermode='closest',
|
||||
plot_bgcolor='rgb(248,248,248)'
|
||||
)
|
||||
|
||||
fig.show()
|
||||
|
||||
return
|
||||
|
||||
def if_root(self, node:Node) -> bool:
|
||||
for p in node.properties:
|
||||
if p.name == "root-certificate":
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_sign_key(self, node:Node):
|
||||
for p in node.properties:
|
||||
if p.name == "signing-key":
|
||||
return p.values
|
||||
|
||||
return None
|
||||
|
||||
def get_nv_ctr(self, node:Node):
|
||||
for nv in node.properties:
|
||||
if nv.name == "antirollback-counter":
|
||||
return nv.values
|
||||
|
||||
return None
|
||||
|
||||
def extract_label(self, label) -> str:
|
||||
if not label:
|
||||
return label
|
||||
return label[0].label.name
|
||||
|
||||
def get_auth_data(self, node:Node):
|
||||
return node.children
|
||||
|
||||
def format_auth_data_val(self, node:Node, cert:Node):
|
||||
type_desc = node.name
|
||||
if "sp_pkg" in type_desc:
|
||||
ptr = removeNumber(type_desc) + "_buf"
|
||||
else:
|
||||
ptr = type_desc + "_buf"
|
||||
len = "(unsigned int)HASH_DER_LEN"
|
||||
if "pk" in type_desc:
|
||||
len = "(unsigned int)PK_DER_LEN"
|
||||
|
||||
# edge case
|
||||
if not self.if_root(cert) and "key_cert" in cert.name:
|
||||
if "content_pk" in ptr:
|
||||
ptr = "content_pk_buf"
|
||||
|
||||
return type_desc, ptr, len
|
||||
|
||||
def get_node(self, nodes: list[Node], name: str) -> Node:
|
||||
for i in nodes:
|
||||
if i.name == name:
|
||||
return i
|
||||
|
||||
def get_certificates(self) -> Node:
|
||||
children = self.tree.children
|
||||
for i in children:
|
||||
if i.name == "cot":
|
||||
return self.get_node(i.children, "manifests")
|
||||
|
||||
def get_images(self)-> Node:
|
||||
children = self.tree.children
|
||||
for i in children:
|
||||
if i.name == "cot":
|
||||
return self.get_node(i.children, "images")
|
||||
|
||||
def get_nv_counters(self) -> Node:
|
||||
children = self.tree.children
|
||||
return self.get_node(children, "non_volatile_counters")
|
||||
|
||||
def get_rot_keys(self) -> Node:
|
||||
children = self.tree.children
|
||||
return self.get_node(children, "rot_keys")
|
||||
|
||||
def get_all_certificates(self) -> Node:
|
||||
cert = self.get_certificates()
|
||||
return cert.children
|
||||
|
||||
def get_all_images(self) -> Node:
|
||||
image = self.get_images()
|
||||
return image.children
|
||||
|
||||
def get_all_nv_counters(self) -> Node:
|
||||
nv = self.get_nv_counters()
|
||||
return nv.children
|
||||
|
||||
def get_all_pks(self) -> Node:
|
||||
pk = self.get_rot_keys()
|
||||
if not pk:
|
||||
return []
|
||||
return pk.children
|
||||
|
||||
def validate_cert(self, node:Node) -> bool:
|
||||
valid = True
|
||||
if not node.has_field("image-id"):
|
||||
print("{} missing mandatory attribute image-id".format(node.name))
|
||||
valid = False
|
||||
|
||||
if not node.has_field("root-certificate"):
|
||||
if not node.has_field("parent"):
|
||||
print("{} missing mandatory attribute parent".format(node.name))
|
||||
valid = False
|
||||
else:
|
||||
# check if refer to non existing parent
|
||||
certs = self.get_all_certificates()
|
||||
found = False
|
||||
for c in certs:
|
||||
if c.name == self.extract_label(node.get_fields("parent")):
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print("{} refer to non existing parent".format(node.name))
|
||||
valid = False
|
||||
|
||||
else:
|
||||
self.has_root = True
|
||||
|
||||
child = node.children
|
||||
if child:
|
||||
for c in child:
|
||||
if not c.has_field("oid"):
|
||||
print("{} missing mandatory attribute oid".format(c.name))
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
|
||||
def validate_img(self, node:Node) -> bool:
|
||||
valid = True
|
||||
if not node.has_field("image-id"):
|
||||
print("{} missing mandatory attribute image-id".format(node.name))
|
||||
valid = False
|
||||
|
||||
if not node.has_field("parent"):
|
||||
print("{} missing mandatory attribute parent".format(node.name))
|
||||
valid = False
|
||||
|
||||
if not node.has_field("hash"):
|
||||
print("{} missing mandatory attribute hash".format(node.name))
|
||||
valid = False
|
||||
|
||||
# check if refer to non existing parent
|
||||
certs = self.get_all_certificates()
|
||||
found = False
|
||||
for c in certs:
|
||||
if c.name == self.extract_label(node.get_fields("parent")):
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print("{} refer to non existing parent".format(node.name))
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
|
||||
def validate_nodes(self) -> bool:
|
||||
valid = True
|
||||
|
||||
if ifdef_stack:
|
||||
print("invalid ifdef macro")
|
||||
valid = False
|
||||
|
||||
certs = self.get_all_certificates()
|
||||
images = self.get_all_images()
|
||||
|
||||
for n in certs:
|
||||
node_valid = self.validate_cert(n)
|
||||
valid = valid and node_valid
|
||||
|
||||
for i in images:
|
||||
node_valid = self.validate_img(i)
|
||||
valid = valid and node_valid
|
||||
|
||||
if not self.has_root:
|
||||
print("missing root certificate")
|
||||
|
||||
return valid
|
||||
|
||||
def extract_licence(self, f):
|
||||
licence = []
|
||||
|
||||
licencereg = re.compile(r'/\*')
|
||||
licenceendReg = re.compile(r'\*/')
|
||||
|
||||
licencePre = False
|
||||
|
||||
for line in f:
|
||||
match = licencereg.search(line)
|
||||
if match != None:
|
||||
licence.append(line)
|
||||
licencePre = True
|
||||
continue
|
||||
|
||||
match = licenceendReg.search(line)
|
||||
if match != None:
|
||||
licence.append(line)
|
||||
licencePre = False
|
||||
return licence
|
||||
|
||||
if licencePre:
|
||||
licence.append(line)
|
||||
else:
|
||||
return licence
|
||||
|
||||
return licence
|
||||
|
||||
def licence_to_c(self, licence, f):
|
||||
if len(licence) != 0:
|
||||
for i in licence:
|
||||
f.write(i)
|
||||
|
||||
f.write("\n")
|
||||
return
|
||||
|
||||
def extract_include(self, f):
|
||||
include = []
|
||||
|
||||
for line in f:
|
||||
if "cot" in line:
|
||||
return include
|
||||
|
||||
if line != "" and "common" not in line and line != "\n":
|
||||
include.append(line)
|
||||
|
||||
return include
|
||||
|
||||
def include_to_c(self, include, f):
|
||||
f.write("#include <stddef.h>\n")
|
||||
f.write("#include <mbedtls/version.h>\n")
|
||||
f.write("#include <common/tbbr/cot_def.h>\n")
|
||||
f.write("#include <drivers/auth/auth_mod.h>\n")
|
||||
f.write("\n")
|
||||
for i in include:
|
||||
f.write(i)
|
||||
f.write("\n")
|
||||
f.write("#include <platform_def.h>\n\n")
|
||||
return
|
||||
|
||||
def generate_header(self, input, output):
|
||||
licence = self.extract_licence(input)
|
||||
include = self.extract_include(input)
|
||||
self.licence_to_c(licence, output)
|
||||
self.include_to_c(include, output)
|
||||
|
||||
def all_cert_to_c(self, f):
|
||||
certs = self.get_all_certificates()
|
||||
for c in certs:
|
||||
self.cert_to_c(c, f)
|
||||
|
||||
f.write("\n")
|
||||
|
||||
def cert_to_c(self, node: Node, f):
|
||||
ifdef = node.get_fields("ifdef")
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
f.write("static const auth_img_desc_t {} = {{\n".format(node.name))
|
||||
f.write("\t.img_id = {},\n".format(node.get_field("image-id").values[0].replace('"', "")))
|
||||
f.write("\t.img_type = IMG_CERT,\n")
|
||||
|
||||
if not self.if_root(node):
|
||||
f.write("\t.parent = &{},\n".format(node.get_field("parent").label.name))
|
||||
else:
|
||||
f.write("\t.parent = NULL,\n")
|
||||
|
||||
sign = self.get_sign_key(node)
|
||||
nv_ctr = self.get_nv_ctr(node)
|
||||
|
||||
if sign or nv_ctr:
|
||||
f.write("\t.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {\n")
|
||||
|
||||
if sign:
|
||||
f.write("\t\t[0] = {\n")
|
||||
f.write("\t\t\t.type = AUTH_METHOD_SIG,\n")
|
||||
f.write("\t\t\t.param.sig = {\n")
|
||||
|
||||
f.write("\t\t\t\t.pk = &{},\n".format(self.extract_label(sign)))
|
||||
f.write("\t\t\t\t.sig = &sig,\n")
|
||||
f.write("\t\t\t\t.alg = &sig_alg,\n")
|
||||
f.write("\t\t\t\t.data = &raw_data\n")
|
||||
f.write("\t\t\t}\n")
|
||||
f.write("\t\t}}{}\n".format("," if nv_ctr else ""))
|
||||
|
||||
if nv_ctr:
|
||||
f.write("\t\t[1] = {\n")
|
||||
f.write("\t\t\t.type = AUTH_METHOD_NV_CTR,\n")
|
||||
f.write("\t\t\t.param.nv_ctr = {\n")
|
||||
|
||||
f.write("\t\t\t\t.cert_nv_ctr = &{},\n".format(self.extract_label(nv_ctr)))
|
||||
f.write("\t\t\t\t.plat_nv_ctr = &{}\n".format(self.extract_label(nv_ctr)))
|
||||
|
||||
f.write("\t\t\t}\n")
|
||||
f.write("\t\t}\n")
|
||||
|
||||
f.write("\t},\n")
|
||||
|
||||
auth_data = self.get_auth_data(node)
|
||||
if auth_data:
|
||||
f.write("\t.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {\n")
|
||||
|
||||
for i, d in enumerate(auth_data):
|
||||
type_desc, ptr, data_len = self.format_auth_data_val(d, node)
|
||||
|
||||
f.write("\t\t[{}] = {{\n".format(i))
|
||||
f.write("\t\t\t.type_desc = &{},\n".format(type_desc))
|
||||
f.write("\t\t\t.data = {\n")
|
||||
|
||||
n = extractNumber(type_desc)
|
||||
if "pkg" not in type_desc or n == -1:
|
||||
f.write("\t\t\t\t.ptr = (void *){},\n".format(ptr))
|
||||
else:
|
||||
f.write("\t\t\t\t.ptr = (void *){}[{}],\n".format(ptr, n-1))
|
||||
|
||||
f.write("\t\t\t\t.len = {}\n".format(data_len))
|
||||
f.write("\t\t\t}\n")
|
||||
|
||||
f.write("\t\t}}{}\n".format("," if i != len(auth_data) - 1 else ""))
|
||||
|
||||
f.write("\t}\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("#endif\n")
|
||||
f.write("\n")
|
||||
|
||||
return
|
||||
|
||||
|
||||
def img_to_c(self, node:Node, f):
|
||||
ifdef = node.get_fields("ifdef")
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
f.write("static const auth_img_desc_t {} = {{\n".format(node.name))
|
||||
f.write("\t.img_id = {},\n".format(node.get_field("image-id").values[0].replace('"', "")))
|
||||
f.write("\t.img_type = IMG_RAW,\n")
|
||||
f.write("\t.parent = &{},\n".format(node.get_field("parent").label.name))
|
||||
f.write("\t.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {\n")
|
||||
|
||||
f.write("\t\t[0] = {\n")
|
||||
f.write("\t\t\t.type = AUTH_METHOD_HASH,\n")
|
||||
f.write("\t\t\t.param.hash = {\n")
|
||||
f.write("\t\t\t\t.data = &raw_data,\n")
|
||||
f.write("\t\t\t\t.hash = &{}\n".format(node.get_field("hash").label.name))
|
||||
f.write("\t\t\t}\n")
|
||||
|
||||
f.write("\t\t}\n")
|
||||
f.write("\t}\n")
|
||||
f.write("};\n\n")
|
||||
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("#endif\n")
|
||||
f.write("\n")
|
||||
|
||||
return
|
||||
|
||||
def all_img_to_c(self, f):
|
||||
images = self.get_all_images()
|
||||
for i in images:
|
||||
self.img_to_c(i, f)
|
||||
|
||||
f.write("\n")
|
||||
|
||||
def nv_to_c(self, f):
|
||||
nv_ctr = self.get_all_nv_counters()
|
||||
|
||||
for nv in nv_ctr:
|
||||
f.write("static auth_param_type_desc_t {} = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_NV_CTR, {});\n".format(nv.name, nv.get_field("oid")))
|
||||
|
||||
f.write("\n")
|
||||
|
||||
return
|
||||
|
||||
def pk_to_c(self, f):
|
||||
pks = self.get_all_pks()
|
||||
|
||||
for p in pks:
|
||||
f.write("static auth_param_type_desc_t {} = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, {});\n".format(p.name, p.get_field("oid")))
|
||||
|
||||
f.write("\n")
|
||||
return
|
||||
|
||||
def buf_to_c(self, f):
|
||||
certs = self.get_all_certificates()
|
||||
|
||||
buffers = {}
|
||||
|
||||
for c in certs:
|
||||
auth_data = self.get_auth_data(c)
|
||||
for a in auth_data:
|
||||
type_desc, ptr, data_len = self.format_auth_data_val(a, c)
|
||||
if ptr not in buffers:
|
||||
buffers[ptr] = c.get_fields("ifdef")
|
||||
|
||||
for key, values in buffers.items():
|
||||
if values:
|
||||
for i in values:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
if "sp_pkg_hash_buf" in key:
|
||||
f.write("static unsigned char {}[MAX_SP_IDS][HASH_DER_LEN];\n".format(key))
|
||||
elif "pk" in key:
|
||||
f.write("static unsigned char {}[PK_DER_LEN];\n".format(key))
|
||||
else:
|
||||
f.write("static unsigned char {}[HASH_DER_LEN];\n".format(key))
|
||||
|
||||
if values:
|
||||
for i in values:
|
||||
f.write("#endif\n")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
def param_to_c(self, f):
|
||||
f.write("static auth_param_type_desc_t subject_pk = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, 0);\n")
|
||||
f.write("static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG, 0);\n")
|
||||
f.write("static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_SIG_ALG, 0);\n")
|
||||
f.write("static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC(AUTH_PARAM_RAW_DATA, 0);\n")
|
||||
f.write("\n")
|
||||
|
||||
certs = self.get_all_certificates()
|
||||
for c in certs:
|
||||
ifdef = c.get_fields("ifdef")
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
hash = c.children
|
||||
for h in hash:
|
||||
name = h.name
|
||||
oid = h.get_field("oid")
|
||||
|
||||
if "pk" in name and "pkg" not in name:
|
||||
f.write("static auth_param_type_desc_t {} = "\
|
||||
"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, {});\n".format(name, oid))
|
||||
elif "hash" in name:
|
||||
f.write("static auth_param_type_desc_t {} = "\
|
||||
"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_HASH, {});\n".format(name, oid))
|
||||
elif "ctr" in name:
|
||||
f.write("static auth_param_type_desc_t {} = "\
|
||||
"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_NV_CTR, {});\n".format(name, oid))
|
||||
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("#endif\n")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
def cot_to_c(self, f):
|
||||
certs = self.get_all_certificates()
|
||||
images = self.get_all_images()
|
||||
|
||||
f.write("static const auth_img_desc_t * const cot_desc[] = {\n")
|
||||
|
||||
for i, c in enumerate(certs):
|
||||
ifdef = c.get_fields("ifdef")
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
f.write("\t[{}] = &{}{}\n".format(c.get_field("image-id").values[0], c.name, ","))
|
||||
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("#endif\n")
|
||||
|
||||
for i, c in enumerate(images):
|
||||
ifdef = c.get_fields("ifdef")
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("{}\n".format(i))
|
||||
|
||||
f.write("\t[{}] = &{}{}\n".format(c.get_field("image-id").values[0], c.name, "," if i != len(images) - 1 else ""))
|
||||
|
||||
if ifdef:
|
||||
for i in ifdef:
|
||||
f.write("#endif\n")
|
||||
|
||||
f.write("};\n\n")
|
||||
f.write("REGISTER_COT(cot_desc);\n")
|
||||
return
|
||||
|
||||
def generate_c_file(self):
|
||||
filename = Path(self.output)
|
||||
filename.parent.mkdir(exist_ok=True, parents=True)
|
||||
output = open(self.output, 'w+')
|
||||
input = open(self.input, "r")
|
||||
|
||||
self.generate_header(input, output)
|
||||
self.buf_to_c(output)
|
||||
self.param_to_c(output)
|
||||
self.nv_to_c(output)
|
||||
self.pk_to_c(output)
|
||||
self.all_cert_to_c(output)
|
||||
self.all_img_to_c(output)
|
||||
self.cot_to_c(output)
|
||||
|
||||
return
|
130
tools/cot_dt2c/cot_dt2c/dt_validator.py
Normal file
130
tools/cot_dt2c/cot_dt2c/dt_validator.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import sys
|
||||
from os import path, walk, mkdir
|
||||
import subprocess
|
||||
from cot_dt2c.pydevicetree import *
|
||||
|
||||
class bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKCYAN = '\033[96m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
class DTTree:
|
||||
def __init__(self, input):
|
||||
self.input = input
|
||||
self.test_dir = "./tmp"
|
||||
self.logging_file = self.test_dir + "/result.log"
|
||||
|
||||
def dtValidate(self):
|
||||
subprocess.run(["rm", "-rf", self.test_dir])
|
||||
|
||||
if not path.exists(self.test_dir):
|
||||
mkdir(self.test_dir)
|
||||
|
||||
if path.isfile(self.input):
|
||||
self.dtValidateFile(self.input, printInfo=True)
|
||||
return
|
||||
|
||||
if path.isdir(self.input):
|
||||
self.dtValidateFiles()
|
||||
return
|
||||
|
||||
def dtValidateFile(self, input, printInfo=False):
|
||||
valid, tree = self.dtParseFile(input, printInfo)
|
||||
|
||||
if not valid:
|
||||
return False
|
||||
|
||||
if input.rfind("/") != -1:
|
||||
filename = self.test_dir + input[input.rfind("/"):]
|
||||
else:
|
||||
filename = self.test_dir + "/" + input
|
||||
|
||||
f = open(filename, "w+")
|
||||
if "/dts-v1/;" not in str(tree):
|
||||
f.write("/dts-v1/;\n\n")
|
||||
f.write(str(tree))
|
||||
f.close()
|
||||
|
||||
if str(tree) == "":
|
||||
return valid
|
||||
|
||||
return valid
|
||||
|
||||
def dtParseFile(self, input, printInfo=False):
|
||||
with open(input, 'r') as f:
|
||||
contents = f.read()
|
||||
|
||||
pos = contents.find("/ {")
|
||||
if pos != -1:
|
||||
contents = contents[pos:]
|
||||
|
||||
try:
|
||||
tree = Devicetree.parseStr(contents)
|
||||
if printInfo:
|
||||
print(bcolors.OKGREEN + "{} parse tree successfully".format(input) + bcolors.ENDC)
|
||||
except Exception as e:
|
||||
if printInfo:
|
||||
print(bcolors.FAIL + "{} parse tree failed:\t{}".format(input, str(e)) + bcolors.ENDC)
|
||||
else:
|
||||
f = open(self.logging_file, "a")
|
||||
f.write("=====================================================================================\n")
|
||||
f.write("{} result:\n".format(input))
|
||||
f.write("{} INVALID:\t{}\n".format(input, str(e)))
|
||||
f.close()
|
||||
return False, None
|
||||
|
||||
return True, tree
|
||||
|
||||
def dtValidateFiles(self):
|
||||
f = []
|
||||
for (dirpath, dirnames, filenames) in walk(self.input):
|
||||
f.extend(filenames)
|
||||
|
||||
allFile = len(f)
|
||||
dtsiFile = 0
|
||||
validFile = 0
|
||||
invalidFile = 0
|
||||
|
||||
for i in f:
|
||||
if (".dtsi" in i or ".dts" in i) and "cot" not in i and "fw-config" not in i:
|
||||
dtsiFile += 1
|
||||
valid = True
|
||||
|
||||
if self.input[-1] == "/":
|
||||
valid = self.dtValidateFile(self.input + i)
|
||||
else:
|
||||
valid = self.dtValidateFile(self.input + "/" + i)
|
||||
|
||||
if valid:
|
||||
validFile += 1
|
||||
else:
|
||||
invalidFile += 1
|
||||
|
||||
print("=====================================================")
|
||||
print("Total File: " + str(allFile))
|
||||
print("Total DT File: " + str(dtsiFile))
|
||||
print("Total Valid File: " + str(validFile))
|
||||
print("Total Invalid File: " + str(invalidFile))
|
||||
|
||||
def dtValidatorMain(input):
|
||||
dt = DTTree(input)
|
||||
dt.dtValidate()
|
||||
|
||||
if __name__=="__main__":
|
||||
if (len(sys.argv) < 2):
|
||||
print("usage: python3 " + sys.argv[0] + " [dtsi file path] or [dtsi folder path]")
|
||||
exit()
|
||||
if len(sys.argv) == 2:
|
||||
dtValidatorMain(sys.argv[1])
|
5
tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py
Normal file
5
tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from cot_dt2c.pydevicetree.ast import Devicetree, Node, Property, Directive, CellArray, LabelReference
|
9
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py
Normal file
9
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from cot_dt2c.pydevicetree.ast.directive import Directive
|
||||
from cot_dt2c.pydevicetree.ast.node import Node, NodeReference, Devicetree
|
||||
from cot_dt2c.pydevicetree.ast.property import PropertyValues, Bytestring, CellArray, StringList, Property, \
|
||||
RegArray, OneString
|
||||
from cot_dt2c.pydevicetree.ast.reference import Label, Path, Reference, LabelReference, PathReference
|
46
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py
Normal file
46
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import Any
|
||||
|
||||
from cot_dt2c.pydevicetree.ast.helpers import formatLevel, wrapStrings
|
||||
|
||||
class Directive:
|
||||
"""Represents a Devicetree directive
|
||||
|
||||
Directives in Devicetree source are statements of the form
|
||||
|
||||
/directive-name/ [option1 [option2 [...]]];
|
||||
|
||||
Common directive examples include:
|
||||
|
||||
/dts-v1/;
|
||||
/include/ "overlay.dtsi";
|
||||
/delete-node/ &uart0;
|
||||
/delete-property/ status;
|
||||
|
||||
Their semantic meaning depends on the directive name, their location in the Devicetree,
|
||||
and their options.
|
||||
"""
|
||||
def __init__(self, directive: str, option: Any = None):
|
||||
"""Create a directive object"""
|
||||
self.directive = directive
|
||||
self.option = option
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<Directive %s>" % self.directive
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_dts()
|
||||
|
||||
def to_dts(self, level: int = 0) -> str:
|
||||
"""Format the Directive in Devicetree Source format"""
|
||||
if isinstance(self.option, list):
|
||||
return formatLevel(level, "%s %s;\n" % (self.directive,
|
||||
wrapStrings(self.option)))
|
||||
if isinstance(self.option, str):
|
||||
if self.directive == "/include/":
|
||||
return formatLevel(level, "%s \"%s\"\n" % (self.directive, self.option))
|
||||
return formatLevel(level, "%s \"%s\";\n" % (self.directive, self.option))
|
||||
return formatLevel(level, "%s;\n" % self.directive)
|
28
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py
Normal file
28
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import List, Any
|
||||
|
||||
from cot_dt2c.pydevicetree.ast.reference import Reference
|
||||
|
||||
def formatLevel(level: int, s: str) -> str:
|
||||
"""Helper to indent a string with a number of tabs"""
|
||||
return "\t" * level + s
|
||||
|
||||
def wrapStrings(values: List[Any], formatHex: bool = False) -> List[Any]:
|
||||
"""Helper to wrap strings in quotes where appropriate"""
|
||||
wrapped = []
|
||||
for v in values:
|
||||
if isinstance(v, Reference):
|
||||
wrapped.append(v.to_dts())
|
||||
elif isinstance(v, str):
|
||||
wrapped.append("\"%s\"" % v)
|
||||
elif isinstance(v, int):
|
||||
if formatHex:
|
||||
wrapped.append("0x%x" % v)
|
||||
else:
|
||||
wrapped.append(str(v))
|
||||
else:
|
||||
wrapped.append(str(v))
|
||||
return wrapped
|
514
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py
Normal file
514
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py
Normal file
|
@ -0,0 +1,514 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import re
|
||||
import os
|
||||
from typing import List, Union, Optional, Iterable, Callable, Any, cast, Pattern
|
||||
|
||||
from cot_dt2c.pydevicetree.ast.helpers import formatLevel
|
||||
from cot_dt2c.pydevicetree.ast.property import Property, PropertyValues, RegArray, RangeArray
|
||||
from cot_dt2c.pydevicetree.ast.directive import Directive
|
||||
from cot_dt2c.pydevicetree.ast.reference import Label, Path, Reference, LabelReference, PathReference
|
||||
|
||||
# Type signature for elements passed to Devicetree constructor
|
||||
ElementList = Iterable[Union['Node', Property, Directive]]
|
||||
|
||||
# Callback type signatures for Devicetree.match() and Devicetree.chosen()
|
||||
MatchFunc = Callable[['Node'], bool]
|
||||
MatchCallback = Optional[Callable[['Node'], None]]
|
||||
ChosenCallback = Optional[Callable[[PropertyValues], None]]
|
||||
|
||||
class Node:
|
||||
"""Represents a Devicetree Node
|
||||
|
||||
A Devicetree Node generally takes the form
|
||||
|
||||
[label:] node-name@unit-address {
|
||||
[directives]
|
||||
[properties]
|
||||
[child nodes]
|
||||
};
|
||||
|
||||
The structure formed by creating trees of Nodes is the bulk of any Devicetree. As the naming
|
||||
system implies, then, each node roughly corresponds to some conceptual device, subsystem of
|
||||
devices, bus, etc.
|
||||
|
||||
Devices can be referenced by label or by path, and are generally uniquely identified by a
|
||||
collection of string identifiers assigned to the "compatible" property.
|
||||
|
||||
For instance, a UART device might look like
|
||||
|
||||
uart0: uart@10013000 {
|
||||
compatible = "sifive,uart0";
|
||||
reg = <0x10013000 0x1000>;
|
||||
reg-names = "control";
|
||||
interrupt-parent = <&plic>;
|
||||
interrupts = <3>;
|
||||
clocks = <&busclk>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
This node can be identified in the following ways:
|
||||
|
||||
- By label: uart0
|
||||
- By path: /path/to/uart@10013000
|
||||
- By name: uart@10013000 (for example when referenced in a /delete-node/ directive)
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, name: str, label: Optional[str], address: Optional[int],
|
||||
properties: List[Property], directives: List[Directive],
|
||||
children: List['Node']):
|
||||
"""Initializes a Devicetree Node
|
||||
|
||||
Also evaluates the /delete-node/ and /delete-property/ directives found in the node
|
||||
and deletes the respective nodes and properties.
|
||||
"""
|
||||
self.name = name
|
||||
self.parent = None # type: Optional['Node']
|
||||
|
||||
self.label = label
|
||||
self.address = address
|
||||
self.properties = properties
|
||||
self.directives = directives
|
||||
self.children = children
|
||||
self.ifdef = []
|
||||
|
||||
for d in self.directives:
|
||||
if d.directive == "/delete-node/":
|
||||
if isinstance(d.option, LabelReference):
|
||||
node = self.get_by_reference(d.option)
|
||||
elif isinstance(d.option, str):
|
||||
node = self.__get_child_by_handle(d.option)
|
||||
if node:
|
||||
self.remove_child(node)
|
||||
elif d.directive == "/delete-property/":
|
||||
# pylint: disable=cell-var-from-loop
|
||||
properties = list(filter(lambda p: p.name == d.option, self.properties))
|
||||
if properties:
|
||||
del self.properties[self.properties.index(properties[0])]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
if self.address:
|
||||
return "<Node %s@%x>" % (self.name, self.address)
|
||||
return "<Node %s>" % self.name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_dts()
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return self.name == other.name and self.address == other.address
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.name, self.address))
|
||||
|
||||
@staticmethod
|
||||
def from_dts(source: str) -> 'Node':
|
||||
"""Create a node from Devicetree Source"""
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from pydevicetree.source import parseNode
|
||||
return parseNode(source)
|
||||
|
||||
def add_child(self, node: 'Node', merge: bool = True):
|
||||
"""Add a child node and merge it into the tree"""
|
||||
node.parent = self
|
||||
self.children.append(node)
|
||||
if merge:
|
||||
self.merge_tree()
|
||||
|
||||
def to_dts(self, level: int = 0) -> str:
|
||||
"""Format the subtree starting at the node as Devicetree Source"""
|
||||
out = ""
|
||||
if isinstance(self.address, int) and self.label:
|
||||
out += formatLevel(level,
|
||||
"%s: %s@%x {\n" % (self.label, self.name, self.address))
|
||||
elif isinstance(self.address, int):
|
||||
out += formatLevel(level, "%s@%x {\n" % (self.name, self.address))
|
||||
elif self.label:
|
||||
out += formatLevel(level, "%s: %s {\n" % (self.label, self.name))
|
||||
elif self.name != "":
|
||||
out += formatLevel(level, "%s {\n" % self.name)
|
||||
|
||||
for d in self.directives:
|
||||
out += d.to_dts(level + 1)
|
||||
for p in self.properties:
|
||||
out += p.to_dts(level + 1)
|
||||
for c in self.children:
|
||||
out += c.to_dts(level + 1)
|
||||
|
||||
if self.name != "":
|
||||
out += formatLevel(level, "};\n")
|
||||
|
||||
return out
|
||||
|
||||
def merge_tree(self):
|
||||
"""Recursively merge child nodes into a single tree
|
||||
|
||||
Parsed Devicetrees can describe the same tree multiple times, adding nodes and properties
|
||||
each time. After parsing, this method is called to recursively merge the tree.
|
||||
"""
|
||||
partitioned_children = []
|
||||
for n in self.children:
|
||||
partitioned_children.append([e for e in self.children if e == n])
|
||||
|
||||
new_children = []
|
||||
for part in partitioned_children:
|
||||
first = part[0]
|
||||
rest = part[1:]
|
||||
if first not in new_children:
|
||||
for n in rest:
|
||||
first.merge(n)
|
||||
new_children.append(first)
|
||||
|
||||
self.children = new_children
|
||||
|
||||
for n in self.children:
|
||||
n.parent = self
|
||||
n.merge_tree()
|
||||
|
||||
def merge(self, other: 'Node'):
|
||||
"""Merge the contents of a node into this node.
|
||||
|
||||
Used by Node.merge_trees()
|
||||
"""
|
||||
if not self.label and other.label:
|
||||
self.label = other.label
|
||||
self.properties += other.properties
|
||||
self.directives += other.directives
|
||||
self.children += other.children
|
||||
self.ifdef += other.ifdef
|
||||
|
||||
def get_path(self, includeAddress: bool = True) -> str:
|
||||
"""Get the path of a node (ex. /cpus/cpu@0)"""
|
||||
if self.name == "/":
|
||||
return ""
|
||||
if self.parent is None:
|
||||
return "/" + self.name
|
||||
if isinstance(self.address, int) and includeAddress:
|
||||
return self.parent.get_path() + "/" + self.name + "@" + ("%x" % self.address)
|
||||
return self.parent.get_path() + "/" + self.name
|
||||
|
||||
def get_by_reference(self, reference: Reference) -> Optional['Node']:
|
||||
"""Get a node from the subtree by reference (ex. &label, &{/path/to/node})"""
|
||||
if isinstance(reference, LabelReference):
|
||||
return self.get_by_label(reference.label)
|
||||
if isinstance(reference, PathReference):
|
||||
return self.get_by_path(reference.path)
|
||||
|
||||
return None
|
||||
|
||||
def get_by_label(self, label: Union[Label, str]) -> Optional['Node']:
|
||||
"""Get a node from the subtree by label"""
|
||||
matching_nodes = list(filter(lambda n: n.label == label, self.child_nodes()))
|
||||
if len(matching_nodes) != 0:
|
||||
return matching_nodes[0]
|
||||
return None
|
||||
|
||||
def __get_child_by_handle(self, handle: str) -> Optional['Node']:
|
||||
"""Get a child node by name or name and unit address"""
|
||||
if '@' in handle:
|
||||
name, addr_s = handle.split('@')
|
||||
address = int(addr_s, base=16)
|
||||
nodes = list(filter(lambda n: n.name == name and n.address == address, self.children))
|
||||
else:
|
||||
name = handle
|
||||
nodes = list(filter(lambda n: n.name == name, self.children))
|
||||
|
||||
if not nodes:
|
||||
return None
|
||||
if len(nodes) > 1:
|
||||
raise Exception("Handle %s is ambiguous!" % handle)
|
||||
return nodes[0]
|
||||
|
||||
def get_by_path(self, path: Union[Path, str]) -> Optional['Node']:
|
||||
"""Get a node in the subtree by path"""
|
||||
matching_nodes = list(filter(lambda n: path == n.get_path(includeAddress=True), \
|
||||
self.child_nodes()))
|
||||
if len(matching_nodes) != 0:
|
||||
return matching_nodes[0]
|
||||
|
||||
matching_nodes = list(filter(lambda n: path == n.get_path(includeAddress=False), \
|
||||
self.child_nodes()))
|
||||
if len(matching_nodes) != 0:
|
||||
return matching_nodes[0]
|
||||
return None
|
||||
|
||||
def filter(self, matchFunc: MatchFunc, cbFunc: MatchCallback = None) -> List['Node']:
|
||||
"""Filter all child nodes by matchFunc
|
||||
|
||||
If cbFunc is provided, this method will iterate over the Nodes selected by matchFunc
|
||||
and call cbFunc on each Node
|
||||
|
||||
Returns a list of all matching Nodes
|
||||
"""
|
||||
nodes = list(filter(matchFunc, self.child_nodes()))
|
||||
|
||||
if cbFunc is not None:
|
||||
for n in nodes:
|
||||
cbFunc(n)
|
||||
|
||||
return nodes
|
||||
|
||||
def match(self, compatible: Pattern, func: MatchCallback = None) -> List['Node']:
|
||||
"""Get a node from the subtree by compatible string
|
||||
|
||||
Accepts a regular expression to match one of the strings in the compatible property.
|
||||
"""
|
||||
regex = re.compile(compatible)
|
||||
|
||||
def match_compat(node: Node) -> bool:
|
||||
compatibles = node.get_fields("compatible")
|
||||
if compatibles is not None:
|
||||
return any(regex.match(c) for c in compatibles)
|
||||
return False
|
||||
|
||||
return self.filter(match_compat, func)
|
||||
|
||||
def child_nodes(self) -> Iterable['Node']:
|
||||
"""Get an iterable over all the nodes in the subtree"""
|
||||
for n in self.children:
|
||||
yield n
|
||||
for m in n.child_nodes():
|
||||
yield m
|
||||
|
||||
def remove_child(self, node):
|
||||
"""Remove a child node"""
|
||||
del self.children[self.children.index(node)]
|
||||
|
||||
def get_fields(self, field_name: str) -> Optional[PropertyValues]:
|
||||
"""Get all the values of a property"""
|
||||
for p in self.properties:
|
||||
if p.name == field_name:
|
||||
return p.values
|
||||
return None
|
||||
|
||||
def has_field(self, field_name: str) -> bool:
|
||||
for p in self.properties:
|
||||
if p.name == field_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_field(self, field_name: str) -> Any:
|
||||
"""Get the first value of a property"""
|
||||
fields = self.get_fields(field_name)
|
||||
if fields is not None:
|
||||
if len(cast(PropertyValues, fields)) != 0:
|
||||
return fields[0]
|
||||
return None
|
||||
|
||||
def get_reg(self) -> Optional[RegArray]:
|
||||
"""If the node defines a `reg` property, return a RegArray for easier querying"""
|
||||
reg = self.get_fields("reg")
|
||||
reg_names = self.get_fields("reg-names")
|
||||
if reg is not None:
|
||||
if reg_names is not None:
|
||||
return RegArray(reg.values, self.address_cells(), self.size_cells(),
|
||||
reg_names.values)
|
||||
return RegArray(reg.values, self.address_cells(), self.size_cells())
|
||||
return None
|
||||
|
||||
def get_ranges(self) -> Optional[RangeArray]:
|
||||
"""If the node defines a `ranges` property, return a RangeArray for easier querying"""
|
||||
ranges = self.get_fields("ranges")
|
||||
child_address_cells = self.get_field("#address-cells")
|
||||
parent_address_cells = self.address_cells()
|
||||
size_cells = self.get_field("#size-cells")
|
||||
if ranges is not None:
|
||||
return RangeArray(ranges.values, child_address_cells, parent_address_cells, size_cells)
|
||||
return None
|
||||
|
||||
def address_cells(self):
|
||||
"""Get the number of address cells
|
||||
|
||||
The #address-cells property is defined by the parent of a node and describes how addresses
|
||||
are encoded in cell arrays. If no property is defined, the default value is 2.
|
||||
"""
|
||||
if self.parent is not None:
|
||||
cells = self.parent.get_field("#address-cells")
|
||||
if cells is not None:
|
||||
return cells
|
||||
return 2
|
||||
return 2
|
||||
|
||||
def size_cells(self):
|
||||
"""Get the number of size cells
|
||||
|
||||
The #size-cells property is defined by the parent of a node and describes how addresses
|
||||
are encoded in cell arrays. If no property is defined, the default value is 1.
|
||||
"""
|
||||
if self.parent is not None:
|
||||
cells = self.parent.get_field("#size-cells")
|
||||
if cells is not None:
|
||||
return cells
|
||||
return 1
|
||||
return 1
|
||||
|
||||
class NodeReference(Node):
|
||||
"""A NodeReference is used to extend the definition of a previously-defined Node
|
||||
|
||||
NodeReferences are commonly used by Devicetree "overlays" to extend the properties of a node
|
||||
or add child devices, such as to a bus like I2C.
|
||||
"""
|
||||
def __init__(self, reference: Reference, properties: List[Property],
|
||||
directives: List[Directive], children: List[Node]):
|
||||
"""Instantiate a Node identified by reference to another node"""
|
||||
self.reference = reference
|
||||
Node.__init__(self, label=None, name="", address=None, properties=properties,
|
||||
directives=directives, children=children)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<NodeReference %s>" % self.reference.to_dts()
|
||||
|
||||
def resolve_reference(self, tree: 'Devicetree') -> Node:
|
||||
"""Given the full tree, get the node being referenced"""
|
||||
node = tree.get_by_reference(self.reference)
|
||||
if node is None:
|
||||
raise Exception("Node reference %s cannot be resolved" % self.reference.to_dts())
|
||||
return cast(Node, node)
|
||||
|
||||
def to_dts(self, level: int = 0) -> str:
|
||||
out = formatLevel(level, self.reference.to_dts() + " {\n")
|
||||
|
||||
for d in self.directives:
|
||||
out += d.to_dts(level + 1)
|
||||
for p in self.properties:
|
||||
out += p.to_dts(level + 1)
|
||||
for c in self.children:
|
||||
out += c.to_dts(level + 1)
|
||||
|
||||
out += formatLevel(level, "};\n")
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Devicetree(Node):
|
||||
"""A Devicetree object describes the full Devicetree tree
|
||||
|
||||
This class encapsulates both the tree itself (starting at the root node /) and any Directives
|
||||
or nodes which exist at the top level of the Devicetree Source files.
|
||||
|
||||
Devicetree Source files can be parsed by calling Devicetree.parseFile().
|
||||
"""
|
||||
def __init__(self, elements: ElementList):
|
||||
"""Instantiate a Devicetree with the list of parsed elements
|
||||
|
||||
Resolves all reference nodes and merges the tree to combine all identical nodes.
|
||||
"""
|
||||
properties = [] # type: List[Property]
|
||||
directives = [] # type: List[Directive]
|
||||
children = [] # type: List[Node]
|
||||
|
||||
for e in elements:
|
||||
if isinstance(e, Node):
|
||||
children.append(cast(Node, e))
|
||||
elif isinstance(e, Property):
|
||||
properties.append(cast(Property, e))
|
||||
elif isinstance(e, Directive):
|
||||
directives.append(cast(Directive, e))
|
||||
|
||||
Node.__init__(self, label=None, name="", address=None,
|
||||
properties=properties, directives=directives, children=children)
|
||||
|
||||
for node in self.children:
|
||||
node.parent = self
|
||||
|
||||
reference_nodes = self.filter(lambda n: isinstance(n, NodeReference))
|
||||
for refnode in reference_nodes:
|
||||
refnode = cast(NodeReference, refnode)
|
||||
|
||||
node = refnode.resolve_reference(self)
|
||||
|
||||
if refnode.parent:
|
||||
cast(Node, refnode.parent).remove_child(refnode)
|
||||
|
||||
node.properties += refnode.properties
|
||||
node.directives += refnode.directives
|
||||
node.children += refnode.children
|
||||
|
||||
self.merge_tree()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
name = self.root().get_field("compatible")
|
||||
return "<Devicetree %s>" % name
|
||||
|
||||
def to_dts(self, level: int = 0) -> str:
|
||||
"""Convert the tree back to Devicetree Source"""
|
||||
out = ""
|
||||
|
||||
for d in self.directives:
|
||||
out += d.to_dts()
|
||||
for p in self.properties:
|
||||
out += p.to_dts()
|
||||
for c in self.children:
|
||||
out += c.to_dts()
|
||||
|
||||
return out
|
||||
|
||||
def get_by_path(self, path: Union[Path, str]) -> Optional[Node]:
|
||||
"""Get a node in the tree by path (ex. /cpus/cpu@0)"""
|
||||
|
||||
# Find and replace all aliases in the path
|
||||
aliases = self.aliases()
|
||||
if aliases:
|
||||
for prop in aliases.properties:
|
||||
if prop.name in path and len(prop.values) > 0:
|
||||
path = path.replace(prop.name, prop.values[0])
|
||||
|
||||
return self.root().get_by_path(path)
|
||||
|
||||
@staticmethod
|
||||
# pylint: disable=arguments-differ
|
||||
def from_dts(dts: str) -> 'Devicetree':
|
||||
"""Parse a string and return a Devicetree object"""
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from pydevicetree.source import parseTree
|
||||
return parseTree(dts)
|
||||
|
||||
@staticmethod
|
||||
def parseFile(filename: str, followIncludes: bool = False) -> 'Devicetree':
|
||||
"""Parse a file and return a Devicetree object"""
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from cot_dt2c.pydevicetree.source.parser import parseTree
|
||||
with open(filename, 'r') as f:
|
||||
contents = f.read()
|
||||
dirname = os.path.dirname(filename)
|
||||
if dirname != "":
|
||||
dirname += "/"
|
||||
return parseTree(contents, dirname, followIncludes)
|
||||
|
||||
@staticmethod
|
||||
def parseStr(input: str, followIncludes: bool = False) -> 'Devicetree':
|
||||
from cot_dt2c.pydevicetree.source.parser import parseTree
|
||||
return parseTree(input, "", followIncludes)
|
||||
|
||||
def all_nodes(self) -> Iterable[Node]:
|
||||
"""Get an iterable over all nodes in the tree"""
|
||||
return self.child_nodes()
|
||||
|
||||
def root(self) -> Node:
|
||||
"""Get the root node of the tree"""
|
||||
for n in self.all_nodes():
|
||||
if n.name == "/":
|
||||
return n
|
||||
raise Exception("Devicetree has no root node!")
|
||||
|
||||
def aliases(self) -> Optional[Node]:
|
||||
"""Get the aliases node of the tree if it exists"""
|
||||
for n in self.all_nodes():
|
||||
if n.name == "aliases":
|
||||
return n
|
||||
return None
|
||||
|
||||
def chosen(self, property_name: str, func: ChosenCallback = None) -> Optional[PropertyValues]:
|
||||
"""Get the values associated with one of the properties in the chosen node"""
|
||||
def match_chosen(node: Node) -> bool:
|
||||
return node.name == "chosen"
|
||||
|
||||
for n in filter(match_chosen, self.all_nodes()):
|
||||
for p in n.properties:
|
||||
if p.name == property_name:
|
||||
if func is not None:
|
||||
func(p.values)
|
||||
return p.values
|
||||
|
||||
return None
|
278
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py
Normal file
278
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py
Normal file
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import List, Any, cast, Tuple, Optional, Iterable
|
||||
from itertools import zip_longest
|
||||
|
||||
from cot_dt2c.pydevicetree.ast.helpers import wrapStrings, formatLevel
|
||||
|
||||
class PropertyValues:
|
||||
"""PropertyValues is the parent class of all values which can be assigned to a Property
|
||||
|
||||
Child classes include
|
||||
|
||||
Bytestring
|
||||
CellArray
|
||||
StringList
|
||||
"""
|
||||
def __init__(self, values: List[Any]):
|
||||
"""Create a PropertyValue"""
|
||||
self.values = values
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<PropertyValues " + self.values.__repr__() + ">"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_dts()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.values)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.values)
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the values in Devicetree Source format"""
|
||||
return ", ".join(wrapStrings(self.values, formatHex))
|
||||
|
||||
def __getitem__(self, key) -> Any:
|
||||
return self.values[key]
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if isinstance(other, PropertyValues):
|
||||
return self.values == other.values
|
||||
return self.values == other
|
||||
|
||||
class Bytestring(PropertyValues):
|
||||
"""A Bytestring is a sequence of bytes
|
||||
|
||||
In Devicetree, Bytestrings are represented as a sequence of two-digit hexadecimal integers,
|
||||
optionally space-separated, enclosed by square brackets:
|
||||
|
||||
[de ad be eef]
|
||||
"""
|
||||
def __init__(self, bytelist: List[int]):
|
||||
"""Create a Bytestring object"""
|
||||
PropertyValues.__init__(self, cast(List[Any], bytearray(bytelist)))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<Bytestring " + str(self.values) + ">"
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the bytestring in Devicetree Source format"""
|
||||
return "[" + " ".join("%02x" % v for v in self.values) + "]"
|
||||
|
||||
class CellArray(PropertyValues):
|
||||
"""A CellArray is an array of integer values
|
||||
|
||||
CellArrays are commonly used as the value of Devicetree properties like `reg` and `interrupts`.
|
||||
The interpretation of each element of a CellArray is device-dependent. For example, the `reg`
|
||||
property encodes a CellArray as a list of tuples (base address, size), while the `interrupts`
|
||||
property encodes a CellArray as simply a list of interrupt line numbers.
|
||||
"""
|
||||
def __init__(self, cells: List[Any]):
|
||||
"""Create a CellArray object"""
|
||||
PropertyValues.__init__(self, cells)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<CellArray " + self.values.__repr__() + ">"
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the cell array in Devicetree Source format"""
|
||||
dtsValues = []
|
||||
for i in self.values:
|
||||
if not isinstance(i, OneString) and not isinstance(i, str):
|
||||
dtsValues.append(i)
|
||||
return "<" + " ".join(wrapStrings(dtsValues, formatHex)) + ">"
|
||||
|
||||
class RegArray(CellArray):
|
||||
"""A RegArray is the CellArray assigned to the reg property"""
|
||||
def __init__(self, cells: List[int],
|
||||
address_cells: int, size_cells: int,
|
||||
names: Optional[List[str]] = None):
|
||||
"""Create a RegArray from a list of ints"""
|
||||
# pylint: disable=too-many-locals
|
||||
CellArray.__init__(self, cells)
|
||||
self.address_cells = address_cells
|
||||
self.size_cells = size_cells
|
||||
|
||||
self.tuples = [] # type: List[Tuple[int, int, Optional[str]]]
|
||||
|
||||
group_size = self.address_cells + self.size_cells
|
||||
|
||||
if len(cells) % group_size != 0:
|
||||
raise Exception("CellArray does not contain enough cells")
|
||||
|
||||
grouped_cells = [cells[i:i+group_size] for i in range(0, len(cells), group_size)]
|
||||
|
||||
if not names:
|
||||
names = []
|
||||
|
||||
for group, name in zip_longest(grouped_cells, cast(Iterable[Any], names)):
|
||||
address = 0
|
||||
a_cells = list(reversed(group[:self.address_cells]))
|
||||
for a, i in zip(a_cells, range(len(a_cells))):
|
||||
address += (1 << (32 * i)) * a
|
||||
|
||||
size = 0
|
||||
s_cells = list(reversed(group[self.address_cells:]))
|
||||
for s, i in zip(s_cells, range(len(s_cells))):
|
||||
size += (1 << (32 * i)) * s
|
||||
|
||||
self.tuples.append(cast(Tuple[int, int, Optional[str]], tuple([address, size, name])))
|
||||
|
||||
def get_by_name(self, name: str) -> Optional[Tuple[int, int]]:
|
||||
"""Returns the (address, size) tuple with a given name"""
|
||||
for t in self.tuples:
|
||||
if t[2] == name:
|
||||
return cast(Tuple[int, int], tuple(t[:2]))
|
||||
return None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<RegArray " + self.values.__repr__() + ">"
|
||||
|
||||
def __iter__(self) -> Iterable[Tuple[int, int]]:
|
||||
return cast(Iterable[Tuple[int, int]], map(lambda t: tuple(t[:2]), self.tuples))
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.tuples)
|
||||
|
||||
def __getitem__(self, key) -> Optional[Tuple[int, int]]:
|
||||
return list(self.__iter__())[key]
|
||||
|
||||
class RangeArray(CellArray):
|
||||
"""A RangeArray is the CellArray assigned to the range property"""
|
||||
def __init__(self, cells: List[int], child_address_cells: int,
|
||||
parent_address_cells: int, size_cells: int):
|
||||
"""Create a RangeArray from a list of ints"""
|
||||
# pylint: disable=too-many-locals
|
||||
CellArray.__init__(self, cells)
|
||||
self.child_address_cells = child_address_cells
|
||||
self.parent_address_cells = parent_address_cells
|
||||
self.size_cells = size_cells
|
||||
|
||||
self.tuples = [] # type: List[Tuple[int, int, int]]
|
||||
|
||||
group_size = self.child_address_cells + self.parent_address_cells + self.size_cells
|
||||
|
||||
if len(cells) % group_size != 0:
|
||||
raise Exception("CellArray does not contain enough cells")
|
||||
|
||||
grouped_cells = [cells[i:i+group_size] for i in range(0, len(cells), group_size)]
|
||||
|
||||
def sum_cells(cells: List[int]):
|
||||
value = 0
|
||||
for cell, index in zip(list(reversed(cells)), range(len(cells))):
|
||||
value += (1 << (32 * index)) * cell
|
||||
return value
|
||||
|
||||
for group in grouped_cells:
|
||||
child_address = sum_cells(group[:self.child_address_cells])
|
||||
parent_address = sum_cells(group[self.child_address_cells: \
|
||||
self.child_address_cells + self.parent_address_cells])
|
||||
size = sum_cells(group[self.child_address_cells + self.parent_address_cells:])
|
||||
|
||||
self.tuples.append(cast(Tuple[int, int, int],
|
||||
tuple([child_address, parent_address, size])))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<RangeArray " + self.values.__repr__() + ">"
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.tuples)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.tuples)
|
||||
|
||||
def __getitem__(self, key) -> Any:
|
||||
return self.tuples[key]
|
||||
|
||||
class StringList(PropertyValues):
|
||||
"""A StringList is a list of null-terminated strings
|
||||
|
||||
The most common use of a StringList in Devicetree is to describe the `compatible` property.
|
||||
"""
|
||||
def __init__(self, strings: List[str]):
|
||||
"""Create a StringList object"""
|
||||
PropertyValues.__init__(self, strings)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<StringList " + self.values.__repr__() + ">"
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the list of strings in Devicetree Source format"""
|
||||
return ", ".join(wrapStrings(self.values))
|
||||
|
||||
class OneString(PropertyValues):
|
||||
def __init__(self, string: str):
|
||||
PropertyValues.__init__(self, string)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.values.__repr__()
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
return super().to_dts(formatHex)
|
||||
|
||||
class Property:
|
||||
"""A Property is a key-value pair for a Devicetree Node
|
||||
|
||||
Properties are used to describe Nodes in the tree. There are many common properties, like
|
||||
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- ranges
|
||||
- interrupt-controller
|
||||
- interrupts
|
||||
- interrupt-parent
|
||||
- clocks
|
||||
- status
|
||||
|
||||
Which might commonly describe many or all nodes in a tree, and there are device, vendor,
|
||||
operating system, runtime-specific properties.
|
||||
|
||||
Properties can possess no value, conveing meaning solely by their presence:
|
||||
|
||||
interrupt-controller;
|
||||
|
||||
Properties can also possess values such as an array of cells, a list of strings, etc.
|
||||
|
||||
reg = <0x10013000 0x1000>;
|
||||
compatible = "sifive,rocket0", "riscv";
|
||||
|
||||
And properties can posses arbitrarily complex values, such as the following from the
|
||||
Devicetree specification:
|
||||
|
||||
example = <0xf00f0000 19>, "a strange property format";
|
||||
"""
|
||||
def __init__(self, name: str, values: PropertyValues):
|
||||
"""Create a Property object"""
|
||||
self.name = name
|
||||
self.values = values
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<Property %s>" % self.name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_dts()
|
||||
|
||||
@staticmethod
|
||||
def from_dts(dts: str) -> 'Property':
|
||||
"""Parse a file and return a Devicetree object"""
|
||||
# pylint: disable=import-outside-toplevel,cyclic-import
|
||||
from pydevicetree.source import parseProperty
|
||||
return parseProperty(dts)
|
||||
|
||||
def to_dts(self, level: int = 0) -> str:
|
||||
"""Format the Property assignment in Devicetree Source format"""
|
||||
if self.name in ["reg", "ranges"]:
|
||||
value = self.values.to_dts(formatHex=True)
|
||||
else:
|
||||
value = self.values.to_dts(formatHex=False)
|
||||
|
||||
if value != "":
|
||||
return formatLevel(level, "%s = %s;\n" % (self.name, value))
|
||||
if self.name == "ifdef":
|
||||
return ""
|
||||
return formatLevel(level, "%s;\n" % self.name)
|
111
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py
Normal file
111
tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import Union, Iterator
|
||||
|
||||
class Label:
|
||||
"""A Label is a unique identifier for a Node
|
||||
|
||||
For example, the following node has the label "uart0":
|
||||
|
||||
uart0: uart@10013000 {
|
||||
...
|
||||
};
|
||||
"""
|
||||
def __init__(self, name: str):
|
||||
"""Create a Label"""
|
||||
self.name = name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<Label " + self.name + ">"
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Label):
|
||||
return self.name == other.name
|
||||
if isinstance(other, str):
|
||||
return self.name == other
|
||||
return False
|
||||
|
||||
def to_dts(self) -> str:
|
||||
"""Format the label in Devicetree Source format"""
|
||||
return self.name + ":"
|
||||
|
||||
class Path:
|
||||
"""A Path uniquely identifies a Node by its parents and (optionally) unit address"""
|
||||
def __init__(self, path: str):
|
||||
"""Create a path out of a string"""
|
||||
self.path = path
|
||||
|
||||
def to_dts(self) -> str:
|
||||
"""Format the Path in Devicetree Source format"""
|
||||
return self.path
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<Path " + self.to_dts() + ">"
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Path):
|
||||
return self.to_dts() == other.to_dts()
|
||||
if isinstance(other, str):
|
||||
return self.to_dts() == other
|
||||
return False
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter(self.path.split("/"))
|
||||
|
||||
def replace(self, old: str, new: str) -> 'Path':
|
||||
"""Replace any elements of the path which match 'old' with a new element 'new'"""
|
||||
return Path(self.path.replace(old, new))
|
||||
|
||||
class Reference:
|
||||
"""A Reference is a Devicetree construct which points to a Node in the tree
|
||||
|
||||
The following are types of references:
|
||||
|
||||
- A reference to a label:
|
||||
|
||||
&my-label;
|
||||
|
||||
- A reference to a node by path:
|
||||
|
||||
&{/path/to/node@deadbeef}
|
||||
|
||||
This is the parent class for both types of references, LabelReference and PathReference
|
||||
"""
|
||||
# pylint: disable=no-self-use
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the Reference in Devicetree Source format"""
|
||||
return ""
|
||||
|
||||
class LabelReference(Reference):
|
||||
"""A LabelReference is a reference to a Node by label"""
|
||||
def __init__(self, label: Union[Label, str]):
|
||||
"""Create a LabelReference from a Label or string"""
|
||||
if isinstance(label, Label):
|
||||
self.label = label
|
||||
elif isinstance(label, str):
|
||||
self.label = Label(label)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<LabelReference " + self.to_dts() + ">"
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the LabelReference in Devicetree Source format"""
|
||||
return "&" + self.label.name
|
||||
|
||||
class PathReference(Reference):
|
||||
"""A PathReference is a reference to a Node by path"""
|
||||
def __init__(self, path: Union[Path, str]):
|
||||
"""Create a PathReference from a Path or string"""
|
||||
if isinstance(path, Path):
|
||||
self.path = path
|
||||
elif isinstance(path, str):
|
||||
self.path = Path(path)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<PathReference " + self.to_dts() + ">"
|
||||
|
||||
def to_dts(self, formatHex: bool = False) -> str:
|
||||
"""Format the PathReference in Devicetree Source format"""
|
||||
return "&{" + self.path.to_dts() + "}"
|
5
tools/cot_dt2c/cot_dt2c/pydevicetree/source/__init__.py
Normal file
5
tools/cot_dt2c/cot_dt2c/pydevicetree/source/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from cot_dt2c.pydevicetree.source.parser import parseTree, parseNode, parseProperty
|
95
tools/cot_dt2c/cot_dt2c/pydevicetree/source/grammar.py
Normal file
95
tools/cot_dt2c/cot_dt2c/pydevicetree/source/grammar.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pyparsing as p # type: ignore
|
||||
|
||||
ENV_CACHE_OPTION = "PYDEVICETREE_CACHE_SIZE_BOUND"
|
||||
|
||||
cache_bound = None
|
||||
if ENV_CACHE_OPTION in os.environ:
|
||||
option = os.environ[ENV_CACHE_OPTION]
|
||||
if option != "None":
|
||||
try:
|
||||
cache_bound = int(option)
|
||||
except ValueError:
|
||||
print("%s requires a valid integer" % ENV_CACHE_OPTION, file=sys.stderr)
|
||||
p.ParserElement.enablePackrat(cache_bound)
|
||||
|
||||
node_name = p.Word(p.alphanums + ",.-+_") ^ p.Literal("/")
|
||||
integer = p.pyparsing_common.integer ^ (p.Literal("0x").suppress() + p.pyparsing_common.hex_integer)
|
||||
unit_address = p.pyparsing_common.hex_integer
|
||||
unit_addresses = p.delimitedList(unit_address("address"), delim=",")
|
||||
node_handle = node_name("node_name") + p.Optional(p.Literal("@") + unit_addresses)
|
||||
property_name = p.Word(p.alphanums + ",.-_+?#")
|
||||
label = p.Word(p.alphanums + "_").setResultsName("label")
|
||||
label_creation = p.Combine(label + p.Literal(":"))
|
||||
string = p.QuotedString(quoteChar='"')
|
||||
stringlist = p.delimitedList(string)
|
||||
node_path = p.Combine(p.Literal("/") + \
|
||||
p.delimitedList(node_handle, delim="/", combine=True)).setResultsName("path")
|
||||
path_reference = p.Literal("&{").suppress() + node_path + p.Literal("}").suppress()
|
||||
label_reference = p.Literal("&").suppress() + label
|
||||
label_raw = p.Word(p.alphanums + "_")
|
||||
reference = path_reference ^ label_reference ^ label_raw
|
||||
include_directive = p.Literal("/include/") + p.QuotedString(quoteChar='"')
|
||||
generic_directive = p.QuotedString(quoteChar="/", unquoteResults=False) + \
|
||||
p.Optional(string ^ property_name ^ node_name ^ reference ^ (integer * 2)) + \
|
||||
p.Literal(";").suppress()
|
||||
directive = include_directive ^ generic_directive
|
||||
|
||||
operator = p.oneOf("~ ! * / + - << >> < <= > >= == != & ^ | && ||")
|
||||
arith_expr = p.Forward()
|
||||
ternary_element = arith_expr ^ integer
|
||||
ternary_expr = ternary_element + p.Literal("?") + ternary_element + p.Literal(":") + ternary_element
|
||||
arith_expr = p.nestedExpr(content=(p.OneOrMore(operator ^ integer) ^ ternary_expr))
|
||||
arth_str = p.Forward()
|
||||
arith_str_expr = p.nestedExpr(content=(p.OneOrMore(operator ^ integer ^ label_raw ^ p.Literal(",")) ^ ternary_expr))
|
||||
|
||||
label_list = p.OneOrMore(p.Combine(label + p.Literal("\n")))
|
||||
|
||||
cell_array = p.Literal("<").suppress() + \
|
||||
p.ZeroOrMore(integer ^ arith_expr ^ arith_str_expr ^ label_list ^ string ^ reference ^ label_creation.suppress()) + \
|
||||
p.Literal(">").suppress()
|
||||
bytestring = p.Literal("[").suppress() + \
|
||||
(p.OneOrMore(p.Word(p.hexnums, exact=2) ^ label_creation.suppress())) + \
|
||||
p.Literal("]").suppress()
|
||||
property_values = p.Forward()
|
||||
property_values = p.delimitedList(property_values ^ cell_array ^ bytestring ^ stringlist ^ \
|
||||
reference ^ label_raw)
|
||||
property_assignment = property_name("property_name") + p.Optional(p.Literal("=").suppress() + \
|
||||
(property_values)).setResultsName("value") + p.Optional(p.Literal(";").suppress())
|
||||
|
||||
ifdef_label = p.ZeroOrMore(p.Word(p.alphanums + " _|//*=/(/)"))
|
||||
ifdef_define = p.Combine(p.Keyword("#if") + ifdef_label)
|
||||
ifdef_end = p.Combine(p.Keyword("#endif") + ifdef_label)
|
||||
ifdef_define_values = p.Forward()
|
||||
ifdef_define_values = p.ZeroOrMore(ifdef_define)
|
||||
ifdef_end_values = p.Forward()
|
||||
ifdef_end_values = p.ZeroOrMore(ifdef_end)
|
||||
|
||||
node_opener = ifdef_define_values + p.Optional(label_creation) + node_handle + p.Literal("{").suppress()
|
||||
node_reference_opener = reference + p.Literal("{").suppress()
|
||||
node_closer = p.Literal("}").suppress() + p.Literal(";").suppress() + ifdef_end_values
|
||||
node_definition = p.Forward()
|
||||
# pylint: disable=expression-not-assigned
|
||||
node_definition << (node_opener ^ node_reference_opener) + \
|
||||
p.ZeroOrMore(property_assignment ^ directive ^ node_definition ^ ifdef_define ^ ifdef_end) + \
|
||||
node_closer
|
||||
|
||||
devicetree = p.ZeroOrMore(directive ^ node_definition)
|
||||
|
||||
devicetree.ignore(p.cStyleComment)
|
||||
devicetree.ignore("//" + p.SkipTo(p.lineEnd))
|
||||
devicetree.ignore("#include" + p.SkipTo(p.lineEnd))
|
||||
devicetree.ignore("#define" + p.SkipTo(p.lineEnd))
|
||||
devicetree.ignore("#else" + p.SkipTo(p.lineEnd))
|
||||
devicetree.ignore("#error" + p.SkipTo(p.lineEnd))
|
||||
devicetree.ignore("#ifndef" + p.SkipTo(p.lineEnd))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
devicetree.parseFile(sys.argv[1]).pprint()
|
238
tools/cot_dt2c/cot_dt2c/pydevicetree/source/parser.py
Normal file
238
tools/cot_dt2c/cot_dt2c/pydevicetree/source/parser.py
Normal file
|
@ -0,0 +1,238 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 SiFive Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from cot_dt2c.pydevicetree.source import grammar
|
||||
from cot_dt2c.pydevicetree.ast import *
|
||||
|
||||
ifdef_stack = []
|
||||
|
||||
def transformNode(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Node"""
|
||||
properties = [e for e in tokens.asList() if isinstance(e, Property)]
|
||||
directives = [e for e in tokens.asList() if isinstance(e, Directive)]
|
||||
children = [e for e in tokens.asList() if isinstance(e, Node)]
|
||||
|
||||
if isinstance(tokens[0], Reference):
|
||||
return NodeReference(tokens[0], properties=properties,
|
||||
directives=directives, children=children)
|
||||
return Node(tokens.node_name, tokens.label, tokens.address, properties=properties,
|
||||
directives=directives, children=children)
|
||||
|
||||
def transformPropertyAssignment(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Property"""
|
||||
for v in tokens.value:
|
||||
if isinstance(v, PropertyValues):
|
||||
return Property(tokens.property_name, v)
|
||||
if isinstance(v, CellArray):
|
||||
return Property(tokens.property_name, v)
|
||||
if isinstance(v, StringList):
|
||||
return Property(tokens.property_name, v)
|
||||
if isinstance(v, Reference):
|
||||
return Property(tokens.property_name, v)
|
||||
|
||||
return Property(tokens.property_name, PropertyValues([]))
|
||||
|
||||
def transformDirective(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Directive"""
|
||||
if len(tokens.asList()) > 1:
|
||||
return Directive(tokens[0], tokens[1])
|
||||
return Directive(tokens[0])
|
||||
|
||||
def evaluateArithExpr(string, location, tokens):
|
||||
"""Evaluates a ParseResult as a python expression"""
|
||||
flat_tokens = list(chain.from_iterable(tokens.asList()))
|
||||
expr = " ".join(str(t) for t in flat_tokens)
|
||||
# pylint: disable=eval-used
|
||||
return eval(expr)
|
||||
|
||||
def transformTernary(string, location, tokens):
|
||||
"""Evaluates a ParseResult as a ternary expression"""
|
||||
# pylint: disable=eval-used
|
||||
return eval(str(tokens[2]) +" if " + str(tokens[0]) + " else " + str(tokens[4]))
|
||||
|
||||
def transformPropertyValues(string, location, tokens):
|
||||
"""Transforms a ParseResult into a PropertyValues"""
|
||||
if len(tokens.asList()) == 1:
|
||||
return tokens.asList()[0]
|
||||
return PropertyValues(tokens.asList())
|
||||
|
||||
def transformStringList(string, location, tokens):
|
||||
"""Transforms a ParseResult into a StringList"""
|
||||
return StringList(tokens.asList())
|
||||
|
||||
def transformString(string, location, token):
|
||||
return OneString(token)
|
||||
|
||||
def transformIfdefMacro(string, location, tokens):
|
||||
tokenlist = tokens.asList()
|
||||
for t in tokenlist:
|
||||
ifdef_stack.append(t)
|
||||
return Property("ifdef", PropertyValues(ifdef_stack.copy()))
|
||||
|
||||
def transformIfdefEnd(string, location, tokens):
|
||||
tokenlist = tokens.asList()
|
||||
for t in tokenlist:
|
||||
ifdef_stack.pop()
|
||||
|
||||
def transformIfdef(string, location, tokens):
|
||||
return Property("ifdef", PropertyValues(tokens))
|
||||
|
||||
def evaluateStrArithExpr(string, location, tokens):
|
||||
"""Evaluates a ParseResult as a python expression"""
|
||||
flat_tokens = list(chain.from_iterable(tokens.asList()))
|
||||
for i, t in enumerate(flat_tokens):
|
||||
if isinstance(t, int):
|
||||
flat_tokens[i] = "(" + str(t) + ")"
|
||||
expr = " ".join(str(t) for t in flat_tokens)
|
||||
# pylint: disable=eval-used
|
||||
return expr
|
||||
|
||||
def transformBytestring(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Bytestring"""
|
||||
inttokens = []
|
||||
for t in tokens.asList():
|
||||
if all(c in "0123456789abcdefABCDEF" for c in t):
|
||||
inttokens.append(int(t, base=16))
|
||||
return Bytestring(inttokens)
|
||||
|
||||
def transformCellArray(string, location, tokens):
|
||||
"""Transforms a ParseResult into a CellArray"""
|
||||
return CellArray(tokens.asList())
|
||||
|
||||
def transformLabel(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Label"""
|
||||
return Label(tokens.label)
|
||||
|
||||
def transformPath(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Path"""
|
||||
path = ""
|
||||
for handle in tokens.path[0].split("/"):
|
||||
if "@" in handle:
|
||||
node, address = handle.split("@")
|
||||
path += "/%s@%x" % (node, int(address))
|
||||
elif handle != "":
|
||||
path += "/" + handle
|
||||
return Path(path)
|
||||
|
||||
def transformPathReference(string, location, tokens):
|
||||
"""Transforms a ParseResult into a PathReference"""
|
||||
return PathReference(tokens[0])
|
||||
|
||||
def transformLabelReference(string, location, tokens):
|
||||
"""Transforms a ParseResult into a LabelReference"""
|
||||
return LabelReference(tokens[0])
|
||||
|
||||
def transformReference(string, location, tokens):
|
||||
"""Transforms a ParseResult into a Reference"""
|
||||
if isinstance(tokens[0], Reference):
|
||||
return tokens[0]
|
||||
return None
|
||||
|
||||
grammar.label.setParseAction(transformLabel)
|
||||
grammar.node_path.setParseAction(transformPath)
|
||||
grammar.path_reference.setParseAction(transformPathReference)
|
||||
grammar.label_reference.setParseAction(transformLabelReference)
|
||||
grammar.reference.setParseAction(transformReference)
|
||||
grammar.node_definition.setParseAction(transformNode)
|
||||
grammar.property_assignment.setParseAction(transformPropertyAssignment)
|
||||
grammar.directive.setParseAction(transformDirective)
|
||||
grammar.arith_expr.setParseAction(evaluateArithExpr)
|
||||
grammar.ternary_expr.setParseAction(transformTernary)
|
||||
grammar.stringlist.setParseAction(transformStringList)
|
||||
grammar.bytestring.setParseAction(transformBytestring)
|
||||
grammar.cell_array.setParseAction(transformCellArray)
|
||||
grammar.property_values.setParseAction(transformPropertyValues)
|
||||
grammar.label_raw.setParseAction(transformString)
|
||||
grammar.ifdef_define_values.setParseAction(transformIfdefMacro)
|
||||
grammar.ifdef_end_values.setParseAction(transformIfdefEnd)
|
||||
grammar.arith_str_expr.setParseAction(transformPropertyValues)
|
||||
|
||||
def printTree(tree, level=0):
|
||||
"""Helper function to print a bunch of elements as a tree"""
|
||||
def printlevel(level, s):
|
||||
print(" " * level + s)
|
||||
|
||||
for item in tree:
|
||||
if isinstance(item, Node):
|
||||
if item.address:
|
||||
printlevel(level, "Node %s@%x" % (item.name, item.address))
|
||||
else:
|
||||
printlevel(level, "Node %s" % item.name)
|
||||
|
||||
if item.label:
|
||||
printlevel(level, " Label: %s" % item.label)
|
||||
|
||||
if item.parent:
|
||||
printlevel(level, " Parent: %s" % item.parent)
|
||||
|
||||
printTree(item.properties, level=(level + 1))
|
||||
|
||||
printTree(item.children, level=(level + 1))
|
||||
elif isinstance(item, Property):
|
||||
if item.values:
|
||||
printlevel(level, "Property %s: %s" % (item.name, item.values))
|
||||
else:
|
||||
printlevel(level, "Property %s" % item.name)
|
||||
elif isinstance(item, Directive):
|
||||
if item.options:
|
||||
printlevel(level, "Directive %s: %s" % (item.directive, item.options))
|
||||
else:
|
||||
printlevel(level, "Directive %s" % item.directive)
|
||||
|
||||
def parentNodes(tree, parent=None):
|
||||
"""Walks a tree and sets Nodes' parent field to point at their parent"""
|
||||
for item in tree:
|
||||
if isinstance(item, Node):
|
||||
item.parent = parent
|
||||
parentNodes(item.children, item)
|
||||
|
||||
def recurseIncludeFiles(elements, pwd):
|
||||
"""Recursively follows and parses /include/ directives an a tree"""
|
||||
for e in elements:
|
||||
if isinstance(e, Directive):
|
||||
if e.directive == "/include/":
|
||||
# Prefix with current directory if path is not absolute
|
||||
if e.option[0] != '/':
|
||||
e.option = pwd + e.option
|
||||
|
||||
with open(e.option, 'r') as f:
|
||||
contents = f.read()
|
||||
|
||||
elements += parseElements(contents)
|
||||
|
||||
del elements[elements.asList().index(e)]
|
||||
|
||||
def parseElements(dts, pwd="", followIncludes=False):
|
||||
"""Parses a string into a list of elements"""
|
||||
elements = grammar.devicetree.parseString(dts, parseAll=True)
|
||||
parentNodes(elements)
|
||||
if followIncludes:
|
||||
recurseIncludeFiles(elements, pwd)
|
||||
return elements
|
||||
|
||||
def parseTree(dts, pwd="", followIncludes=False):
|
||||
"""Parses a string into a full Devicetree"""
|
||||
return Devicetree(parseElements(dts, pwd, followIncludes))
|
||||
|
||||
def parseNode(dts):
|
||||
"""Parses a string into a Devictreee Node"""
|
||||
return grammar.node_definition.parseString(dts, parseAll=True)[0]
|
||||
|
||||
def parseProperty(dts):
|
||||
"""Parses a string into a Devicetree Property"""
|
||||
return grammar.property_assignment.parseString(dts, parseAll=True)[0]
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1], 'r') as f:
|
||||
dts = f.read()
|
||||
tree = parseTree(dts)
|
||||
printTree(tree)
|
||||
print(tree)
|
||||
else:
|
||||
print("Please pass the devicetree source file as an argument")
|
||||
sys.exit(1)
|
60
tools/cot_dt2c/pyproject.toml
Normal file
60
tools/cot_dt2c/pyproject.toml
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Poetry pyproject.toml: https://python-poetry.org/docs/pyproject/
|
||||
[build-system]
|
||||
requires = ["poetry_core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry]
|
||||
name = "cot_dt2c"
|
||||
version = "0.1.0"
|
||||
description = "CoT-dt2c Tool is a python script to convert CoT DT file into corresponding C file"
|
||||
authors = ["Arm Ltd <tf-a@lists.trustedfirmware.org>"]
|
||||
license = "BSD-3"
|
||||
repository = "https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/"
|
||||
homepage = "https://trustedfirmware-a.readthedocs.io/en/latest/index.html"
|
||||
|
||||
# Pypi classifiers: https://pypi.org/classifiers/
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
]
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
click = "^8.1.7"
|
||||
pyparsing = "^2.4.7"
|
||||
plotly = "^5.23.0"
|
||||
pandas = "^2.2.2"
|
||||
igraph = "^0.11.6"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
mypy = "^0.910"
|
||||
pytest = "^6.2.5"
|
||||
pyparsing = "^2.4.7"
|
||||
plotly = "^5.23.0"
|
||||
pandas = "^2.2.2"
|
||||
igraph = "^0.11.6"
|
||||
|
||||
[tool.mypy]
|
||||
# https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file
|
||||
python_version = 3.8
|
||||
pretty = true
|
||||
show_traceback = true
|
||||
color_output = true
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["tests"]
|
||||
|
||||
[coverage.paths]
|
||||
source = "cot_dt2c"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
# Entry points for the package https://python-poetry.org/docs/pyproject/#scripts
|
||||
# "cot-dt2c" = "cot_dt2c.__main__:cli"
|
||||
"cot-dt2c" = "cot_dt2c.__main__:cli"
|
6
tools/cot_dt2c/requirements.txt
Normal file
6
tools/cot_dt2c/requirements.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
mypy
|
||||
pylint
|
||||
pyparsing
|
||||
igraph
|
||||
pandas
|
||||
plotly
|
58
tools/cot_dt2c/tests/test.dtsi
Normal file
58
tools/cot_dt2c/tests/test.dtsi
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a valid CoT DT file
|
||||
*
|
||||
*/
|
||||
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
#if defined(test)
|
||||
example_cert: example_cert {
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example {
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
example_ctr: example_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
example_pk: example_pk {
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
69
tools/cot_dt2c/tests/test2.dtsi
Normal file
69
tools/cot_dt2c/tests/test2.dtsi
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a valid CoT DT file
|
||||
*
|
||||
*/
|
||||
|
||||
#if test
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
#endif
|
||||
|
||||
cot
|
||||
{
|
||||
manifests
|
||||
{
|
||||
compatible = "arm, cert-descs";
|
||||
#if defined (test)
|
||||
example_cert: example_cert
|
||||
{
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images
|
||||
{
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example
|
||||
{
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters
|
||||
{
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
example_ctr: example_ctr
|
||||
{
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys
|
||||
{
|
||||
example_pk: example_pk
|
||||
{
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
57
tools/cot_dt2c/tests/test_invalid_bracket.dtsi
Normal file
57
tools/cot_dt2c/tests/test_invalid_bracket.dtsi
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there is
|
||||
* unmatching bracket
|
||||
*
|
||||
*/
|
||||
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
example_cert: example_cert {
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example {
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
example_ctr: example_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
example_pk: example_pk {
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
59
tools/cot_dt2c/tests/test_invalid_ifdef.dtsi
Normal file
59
tools/cot_dt2c/tests/test_invalid_ifdef.dtsi
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there is
|
||||
* unmatching ifdef macro
|
||||
*
|
||||
*/
|
||||
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
example_cert: example_cert {
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(test)
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example {
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
example_ctr: example_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
example_pk: example_pk {
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
61
tools/cot_dt2c/tests/test_invalid_ifdef2.dtsi
Normal file
61
tools/cot_dt2c/tests/test_invalid_ifdef2.dtsi
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there is
|
||||
* unmatching ifdef macro
|
||||
*
|
||||
*/
|
||||
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
example_cert: example_cert {
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(test)
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example {
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
example_ctr: example_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
example_pk: example_pk {
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
267
tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi
Normal file
267
tools/cot_dt2c/tests/test_invalid_missing_attribute.dtsi
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there
|
||||
* are image/certificate that missing mandantory attributes
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tools_share/cca_oid.h>
|
||||
#include <common/tbbr/tbbr_img_def.h>
|
||||
#include <common/nv_cntr_ids.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
cca_content_cert: cca_content_cert {
|
||||
root-certificate;
|
||||
antirollback-counter = <&cca_nv_ctr>;
|
||||
|
||||
tb_fw_hash: tb_fw_hash {
|
||||
oid = TRUSTED_BOOT_FW_HASH_OID;
|
||||
};
|
||||
tb_fw_config_hash: tb_fw_config_hash {
|
||||
oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
hw_config_hash: hw_config_hash {
|
||||
};
|
||||
fw_config_hash: fw_config_hash {
|
||||
oid = FW_CONFIG_HASH_OID;
|
||||
};
|
||||
soc_fw_hash: soc_fw_hash {
|
||||
oid = SOC_AP_FW_HASH_OID;
|
||||
};
|
||||
soc_fw_config_hash: soc_fw_config_hash {
|
||||
oid = SOC_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
rmm_hash: rmm_hash {
|
||||
oid = RMM_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
core_swd_key_cert: core_swd_key_cert {
|
||||
root-certificate;
|
||||
image-id = <CORE_SWD_KEY_CERT_ID>;
|
||||
signing-key = <&swd_rot_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
core_swd_pk: core_swd_pk {
|
||||
oid = CORE_SWD_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
trusted_os_fw_content_cert: trusted_os_fw_content_cert {
|
||||
image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
tos_fw_hash: tos_fw_hash {
|
||||
oid = TRUSTED_OS_FW_HASH_OID;
|
||||
};
|
||||
tos_fw_config_hash: tos_fw_config_hash {
|
||||
oid = TRUSTED_OS_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_key_cert: plat_key_cert {
|
||||
root-certificate;
|
||||
image-id = <PLAT_KEY_CERT_ID>;
|
||||
signing-key = <&prot_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
plat_pk: plat_pk {
|
||||
oid = PLAT_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
non_trusted_fw_content_cert: non_trusted_fw_content_cert {
|
||||
image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>;
|
||||
parent = <&plat_key_cert>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
nt_world_bl_hash: nt_world_bl_hash {
|
||||
oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID;
|
||||
};
|
||||
nt_fw_config_hash: nt_fw_config_hash {
|
||||
oid = NON_TRUSTED_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sip_sp_content_cert: sip_sp_content_cert {
|
||||
image-id = <SIP_SP_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
sp_pkg1_hash: sp_pkg1_hash {
|
||||
oid = SP_PKG1_HASH_OID;
|
||||
};
|
||||
sp_pkg2_hash: sp_pkg2_hash {
|
||||
oid = SP_PKG2_HASH_OID;
|
||||
};
|
||||
sp_pkg3_hash: sp_pkg3_hash {
|
||||
oid = SP_PKG3_HASH_OID;
|
||||
};
|
||||
sp_pkg4_hash: sp_pkg4_hash {
|
||||
oid = SP_PKG4_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_sp_content_cert: plat_sp_content_cert {
|
||||
parent = <&plat_key_cert>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
sp_pkg5_hash: sp_pkg5_hash {
|
||||
oid = SP_PKG5_HASH_OID;
|
||||
};
|
||||
sp_pkg6_hash: sp_pkg6_hash {
|
||||
oid = SP_PKG6_HASH_OID;
|
||||
};
|
||||
sp_pkg7_hash: sp_pkg7_hash {
|
||||
oid = SP_PKG7_HASH_OID;
|
||||
};
|
||||
sp_pkg8_hash: sp_pkg8_hash {
|
||||
oid = SP_PKG8_HASH_OID;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
hw_config {
|
||||
image-id = <HW_CONFIG_ID>;
|
||||
hash = <&hw_config_hash>;
|
||||
};
|
||||
|
||||
bl31_image {
|
||||
image-id = <BL31_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_hash>;
|
||||
};
|
||||
|
||||
soc_fw_config {
|
||||
image-id = <SOC_FW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_config_hash>;
|
||||
};
|
||||
|
||||
rmm_image {
|
||||
image-id = <RMM_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&rmm_hash>;
|
||||
};
|
||||
|
||||
bl32_image {
|
||||
image-id = <BL32_IMAGE_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_hash>;
|
||||
};
|
||||
|
||||
tos_fw_config {
|
||||
image-id = <TOS_FW_CONFIG_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_config_hash>;
|
||||
};
|
||||
|
||||
bl33_image {
|
||||
image-id = <BL33_IMAGE_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_world_bl_hash>;
|
||||
};
|
||||
|
||||
nt_fw_config {
|
||||
image-id = <NT_FW_CONFIG_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_fw_config_hash>;
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sp_pkg1 {
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg1_hash>;
|
||||
};
|
||||
|
||||
sp_pkg2 {
|
||||
image-id = <SP_PKG2_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg2_hash>;
|
||||
};
|
||||
|
||||
sp_pkg3 {
|
||||
image-id = <SP_PKG3_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg3_hash>;
|
||||
};
|
||||
|
||||
sp_pkg4 {
|
||||
image-id = <SP_PKG4_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg4_hash>;
|
||||
};
|
||||
|
||||
sp_pkg5 {
|
||||
image-id = <SP_PKG5_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg5_hash>;
|
||||
};
|
||||
|
||||
sp_pkg6 {
|
||||
image-id = <SP_PKG6_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg6_hash>;
|
||||
};
|
||||
|
||||
sp_pkg7 {
|
||||
image-id = <SP_PKG7_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg7_hash>;
|
||||
};
|
||||
|
||||
sp_pkg8 {
|
||||
image-id = <SP_PKG8_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg8_hash>;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cca_nv_ctr: cca_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
trusted_nv_ctr: trusted_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
non_trusted_nv_ctr: non_trusted_nv_ctr {
|
||||
id = <NON_TRUSTED_NV_CTR_ID>;
|
||||
oid = NON_TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
swd_rot_pk: swd_rot_pk {
|
||||
oid = SWD_ROT_PK_OID;
|
||||
};
|
||||
prot_pk: prot_pk {
|
||||
oid = PROT_PK_OID;
|
||||
};
|
||||
};
|
269
tools/cot_dt2c/tests/test_invalid_missing_attribute2.dtsi
Normal file
269
tools/cot_dt2c/tests/test_invalid_missing_attribute2.dtsi
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there
|
||||
* are image/certificate that points to invalid parent
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tools_share/cca_oid.h>
|
||||
#include <common/tbbr/tbbr_img_def.h>
|
||||
#include <common/nv_cntr_ids.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
cca_content_cert: cca_content_cert {
|
||||
root-certificate;
|
||||
image-id =<CCA_CONTENT_CERT_ID>;
|
||||
antirollback-counter = <&cca_nv_ctr>;
|
||||
|
||||
tb_fw_hash: tb_fw_hash {
|
||||
oid = TRUSTED_BOOT_FW_HASH_OID;
|
||||
};
|
||||
tb_fw_config_hash: tb_fw_config_hash {
|
||||
oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
hw_config_hash: hw_config_hash {
|
||||
oid = HW_CONFIG_HASH_OID;
|
||||
};
|
||||
fw_config_hash: fw_config_hash {
|
||||
oid = FW_CONFIG_HASH_OID;
|
||||
};
|
||||
soc_fw_hash: soc_fw_hash {
|
||||
oid = SOC_AP_FW_HASH_OID;
|
||||
};
|
||||
soc_fw_config_hash: soc_fw_config_hash {
|
||||
oid = SOC_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
rmm_hash: rmm_hash {
|
||||
oid = RMM_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
core_swd_key_cert: core_swd_key_cert {
|
||||
root-certificate;
|
||||
image-id = <CORE_SWD_KEY_CERT_ID>;
|
||||
signing-key = <&swd_rot_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
core_swd_pk: core_swd_pk {
|
||||
oid = CORE_SWD_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
trusted_os_fw_content_cert: trusted_os_fw_content_cert {
|
||||
image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
tos_fw_hash: tos_fw_hash {
|
||||
oid = TRUSTED_OS_FW_HASH_OID;
|
||||
};
|
||||
tos_fw_config_hash: tos_fw_config_hash {
|
||||
oid = TRUSTED_OS_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_key_cert: plat_key_cert {
|
||||
root-certificate;
|
||||
image-id = <PLAT_KEY_CERT_ID>;
|
||||
signing-key = <&prot_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
plat_pk: plat_pk {
|
||||
oid = PLAT_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
non_trusted_fw_content_cert: non_trusted_fw_content_cert {
|
||||
image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
nt_world_bl_hash: nt_world_bl_hash {
|
||||
oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID;
|
||||
};
|
||||
nt_fw_config_hash: nt_fw_config_hash {
|
||||
oid = NON_TRUSTED_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sip_sp_content_cert: sip_sp_content_cert {
|
||||
image-id = <SIP_SP_CONTENT_CERT_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
sp_pkg1_hash: sp_pkg1_hash {
|
||||
oid = SP_PKG1_HASH_OID;
|
||||
};
|
||||
sp_pkg2_hash: sp_pkg2_hash {
|
||||
oid = SP_PKG2_HASH_OID;
|
||||
};
|
||||
sp_pkg3_hash: sp_pkg3_hash {
|
||||
oid = SP_PKG3_HASH_OID;
|
||||
};
|
||||
sp_pkg4_hash: sp_pkg4_hash {
|
||||
oid = SP_PKG4_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_sp_content_cert: plat_sp_content_cert {
|
||||
image-id = <PLAT_SP_CONTENT_CERT_ID>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
sp_pkg5_hash: sp_pkg5_hash {
|
||||
oid = SP_PKG5_HASH_OID;
|
||||
};
|
||||
sp_pkg6_hash: sp_pkg6_hash {
|
||||
oid = SP_PKG6_HASH_OID;
|
||||
};
|
||||
sp_pkg7_hash: sp_pkg7_hash {
|
||||
oid = SP_PKG7_HASH_OID;
|
||||
};
|
||||
sp_pkg8_hash: sp_pkg8_hash {
|
||||
oid = SP_PKG8_HASH_OID;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
hw_config {
|
||||
image-id = <HW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&hw_config_hash>;
|
||||
};
|
||||
|
||||
bl31_image {
|
||||
image-id = <BL31_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_hash>;
|
||||
};
|
||||
|
||||
soc_fw_config {
|
||||
image-id = <SOC_FW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_config_hash>;
|
||||
};
|
||||
|
||||
rmm_image {
|
||||
image-id = <RMM_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&rmm_hash>;
|
||||
};
|
||||
|
||||
bl32_image {
|
||||
image-id = <BL32_IMAGE_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_hash>;
|
||||
};
|
||||
|
||||
tos_fw_config {
|
||||
image-id = <TOS_FW_CONFIG_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_config_hash>;
|
||||
};
|
||||
|
||||
bl33_image {
|
||||
image-id = <BL33_IMAGE_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_world_bl_hash>;
|
||||
};
|
||||
|
||||
nt_fw_config {
|
||||
image-id = <NT_FW_CONFIG_ID>;
|
||||
hash = <&nt_fw_config_hash>;
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sp_pkg1 {
|
||||
image-id = <SP_PKG1_ID>;
|
||||
hash = <&sp_pkg1_hash>;
|
||||
};
|
||||
|
||||
sp_pkg2 {
|
||||
image-id = <SP_PKG2_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg2_hash>;
|
||||
};
|
||||
|
||||
sp_pkg3 {
|
||||
image-id = <SP_PKG3_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg3_hash>;
|
||||
};
|
||||
|
||||
sp_pkg4 {
|
||||
image-id = <SP_PKG4_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg4_hash>;
|
||||
};
|
||||
|
||||
sp_pkg5 {
|
||||
image-id = <SP_PKG5_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg5_hash>;
|
||||
};
|
||||
|
||||
sp_pkg6 {
|
||||
image-id = <SP_PKG6_ID>;
|
||||
parent = <&wrong_parent>;
|
||||
hash = <&sp_pkg6_hash>;
|
||||
};
|
||||
|
||||
sp_pkg7 {
|
||||
image-id = <SP_PKG7_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg7_hash>;
|
||||
};
|
||||
|
||||
sp_pkg8 {
|
||||
image-id = <SP_PKG8_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg8_hash>;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cca_nv_ctr: cca_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
trusted_nv_ctr: trusted_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
non_trusted_nv_ctr: non_trusted_nv_ctr {
|
||||
id = <NON_TRUSTED_NV_CTR_ID>;
|
||||
oid = NON_TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
swd_rot_pk: swd_rot_pk {
|
||||
oid = SWD_ROT_PK_OID;
|
||||
};
|
||||
prot_pk: prot_pk {
|
||||
oid = PROT_PK_OID;
|
||||
};
|
||||
};
|
56
tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi
Normal file
56
tools/cot_dt2c/tests/test_invalid_missing_ctr.dtsi
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there
|
||||
* are image/certificate that missing definition of
|
||||
* nv counters
|
||||
*
|
||||
*/
|
||||
|
||||
#include <example/example.h>
|
||||
#include <example/example/example.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
example_cert: example_cert {
|
||||
root-certificate;
|
||||
image-id =<EXAMPLE_ID>;
|
||||
signing-key = <&swd_rot_pk>;
|
||||
antirollback-counter = <&example_ctr>;
|
||||
|
||||
example_hash: example_hash
|
||||
{
|
||||
oid = EXAMPLE_HASH_ID;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
example {
|
||||
image-id = <EXAMPLE_ID>;
|
||||
parent = <&example_cert>;
|
||||
hash = <&example_hash>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
example_pk: example_pk {
|
||||
oid = EXAMPLE_PK_OID;
|
||||
};
|
||||
};
|
242
tools/cot_dt2c/tests/test_invalid_missing_root.dtsi
Normal file
242
tools/cot_dt2c/tests/test_invalid_missing_root.dtsi
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there
|
||||
* are image/certificate that missing root certificate
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tools_share/cca_oid.h>
|
||||
#include <common/tbbr/tbbr_img_def.h>
|
||||
#include <common/nv_cntr_ids.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
core_swd_key_cert: core_swd_key_cert {
|
||||
image-id = <CORE_SWD_KEY_CERT_ID>;
|
||||
signing-key = <&swd_rot_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
core_swd_pk: core_swd_pk {
|
||||
oid = CORE_SWD_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
trusted_os_fw_content_cert: trusted_os_fw_content_cert {
|
||||
image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
tos_fw_hash: tos_fw_hash {
|
||||
oid = TRUSTED_OS_FW_HASH_OID;
|
||||
};
|
||||
tos_fw_config_hash: tos_fw_config_hash {
|
||||
oid = TRUSTED_OS_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_key_cert: plat_key_cert {
|
||||
image-id = <PLAT_KEY_CERT_ID>;
|
||||
signing-key = <&prot_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
plat_pk: plat_pk {
|
||||
oid = PLAT_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
non_trusted_fw_content_cert: non_trusted_fw_content_cert {
|
||||
image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>;
|
||||
parent = <&plat_key_cert>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
nt_world_bl_hash: nt_world_bl_hash {
|
||||
oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID;
|
||||
};
|
||||
nt_fw_config_hash: nt_fw_config_hash {
|
||||
oid = NON_TRUSTED_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sip_sp_content_cert: sip_sp_content_cert {
|
||||
image-id = <SIP_SP_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
sp_pkg1_hash: sp_pkg1_hash {
|
||||
oid = SP_PKG1_HASH_OID;
|
||||
};
|
||||
sp_pkg2_hash: sp_pkg2_hash {
|
||||
oid = SP_PKG2_HASH_OID;
|
||||
};
|
||||
sp_pkg3_hash: sp_pkg3_hash {
|
||||
oid = SP_PKG3_HASH_OID;
|
||||
};
|
||||
sp_pkg4_hash: sp_pkg4_hash {
|
||||
oid = SP_PKG4_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_sp_content_cert: plat_sp_content_cert {
|
||||
image-id = <PLAT_SP_CONTENT_CERT_ID>;
|
||||
parent = <&plat_key_cert>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
sp_pkg5_hash: sp_pkg5_hash {
|
||||
oid = SP_PKG5_HASH_OID;
|
||||
};
|
||||
sp_pkg6_hash: sp_pkg6_hash {
|
||||
oid = SP_PKG6_HASH_OID;
|
||||
};
|
||||
sp_pkg7_hash: sp_pkg7_hash {
|
||||
oid = SP_PKG7_HASH_OID;
|
||||
};
|
||||
sp_pkg8_hash: sp_pkg8_hash {
|
||||
oid = SP_PKG8_HASH_OID;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
hw_config {
|
||||
image-id = <HW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&hw_config_hash>;
|
||||
};
|
||||
|
||||
bl31_image {
|
||||
image-id = <BL31_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_hash>;
|
||||
};
|
||||
|
||||
soc_fw_config {
|
||||
image-id = <SOC_FW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_config_hash>;
|
||||
};
|
||||
|
||||
rmm_image {
|
||||
image-id = <RMM_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&rmm_hash>;
|
||||
};
|
||||
|
||||
bl32_image {
|
||||
image-id = <BL32_IMAGE_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_hash>;
|
||||
};
|
||||
|
||||
tos_fw_config {
|
||||
image-id = <TOS_FW_CONFIG_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_config_hash>;
|
||||
};
|
||||
|
||||
bl33_image {
|
||||
image-id = <BL33_IMAGE_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_world_bl_hash>;
|
||||
};
|
||||
|
||||
nt_fw_config {
|
||||
image-id = <NT_FW_CONFIG_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_fw_config_hash>;
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sp_pkg1 {
|
||||
image-id = <SP_PKG1_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg1_hash>;
|
||||
};
|
||||
|
||||
sp_pkg2 {
|
||||
image-id = <SP_PKG2_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg2_hash>;
|
||||
};
|
||||
|
||||
sp_pkg3 {
|
||||
image-id = <SP_PKG3_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg3_hash>;
|
||||
};
|
||||
|
||||
sp_pkg4 {
|
||||
image-id = <SP_PKG4_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg4_hash>;
|
||||
};
|
||||
|
||||
sp_pkg5 {
|
||||
image-id = <SP_PKG5_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg5_hash>;
|
||||
};
|
||||
|
||||
sp_pkg6 {
|
||||
image-id = <SP_PKG6_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg6_hash>;
|
||||
};
|
||||
|
||||
sp_pkg7 {
|
||||
image-id = <SP_PKG7_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg7_hash>;
|
||||
};
|
||||
|
||||
sp_pkg8 {
|
||||
image-id = <SP_PKG8_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg8_hash>;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cca_nv_ctr: cca_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
trusted_nv_ctr: trusted_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
non_trusted_nv_ctr: non_trusted_nv_ctr {
|
||||
id = <NON_TRUSTED_NV_CTR_ID>;
|
||||
oid = NON_TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
swd_rot_pk: swd_rot_pk {
|
||||
oid = SWD_ROT_PK_OID;
|
||||
};
|
||||
prot_pk: prot_pk {
|
||||
oid = PROT_PK_OID;
|
||||
};
|
||||
};
|
269
tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi
Normal file
269
tools/cot_dt2c/tests/test_invalid_undefined_parent.dtsi
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* This file provide a malformed CoT DT file that there
|
||||
* are image/certificate that points to invalid parent
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tools_share/cca_oid.h>
|
||||
#include <common/tbbr/tbbr_img_def.h>
|
||||
#include <common/nv_cntr_ids.h>
|
||||
|
||||
cot {
|
||||
manifests {
|
||||
compatible = "arm, cert-descs";
|
||||
|
||||
cca_content_cert: cca_content_cert {
|
||||
root-certificate;
|
||||
image-id =<CCA_CONTENT_CERT_ID>;
|
||||
antirollback-counter = <&cca_nv_ctr>;
|
||||
|
||||
tb_fw_hash: tb_fw_hash {
|
||||
oid = TRUSTED_BOOT_FW_HASH_OID;
|
||||
};
|
||||
tb_fw_config_hash: tb_fw_config_hash {
|
||||
oid = TRUSTED_BOOT_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
hw_config_hash: hw_config_hash {
|
||||
oid = HW_CONFIG_HASH_OID;
|
||||
};
|
||||
fw_config_hash: fw_config_hash {
|
||||
oid = FW_CONFIG_HASH_OID;
|
||||
};
|
||||
soc_fw_hash: soc_fw_hash {
|
||||
oid = SOC_AP_FW_HASH_OID;
|
||||
};
|
||||
soc_fw_config_hash: soc_fw_config_hash {
|
||||
oid = SOC_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
rmm_hash: rmm_hash {
|
||||
oid = RMM_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
core_swd_key_cert: core_swd_key_cert {
|
||||
root-certificate;
|
||||
image-id = <CORE_SWD_KEY_CERT_ID>;
|
||||
signing-key = <&swd_rot_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
core_swd_pk: core_swd_pk {
|
||||
oid = CORE_SWD_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
trusted_os_fw_content_cert: trusted_os_fw_content_cert {
|
||||
image-id = <TRUSTED_OS_FW_CONTENT_CERT_ID>;
|
||||
parent = <&core_swd_key_cert>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
tos_fw_hash: tos_fw_hash {
|
||||
oid = TRUSTED_OS_FW_HASH_OID;
|
||||
};
|
||||
tos_fw_config_hash: tos_fw_config_hash {
|
||||
oid = TRUSTED_OS_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_key_cert: plat_key_cert {
|
||||
root-certificate;
|
||||
image-id = <PLAT_KEY_CERT_ID>;
|
||||
signing-key = <&prot_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
plat_pk: plat_pk {
|
||||
oid = PLAT_PK_OID;
|
||||
};
|
||||
};
|
||||
|
||||
non_trusted_fw_content_cert: non_trusted_fw_content_cert {
|
||||
image-id = <NON_TRUSTED_FW_CONTENT_CERT_ID>;
|
||||
parent = <&wrong_parent>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
nt_world_bl_hash: nt_world_bl_hash {
|
||||
oid = NON_TRUSTED_WORLD_BOOTLOADER_HASH_OID;
|
||||
};
|
||||
nt_fw_config_hash: nt_fw_config_hash {
|
||||
oid = NON_TRUSTED_FW_CONFIG_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sip_sp_content_cert: sip_sp_content_cert {
|
||||
image-id = <SIP_SP_CONTENT_CERT_ID>;
|
||||
parent = <&wrong_parent>;
|
||||
signing-key = <&core_swd_pk>;
|
||||
antirollback-counter = <&trusted_nv_ctr>;
|
||||
|
||||
sp_pkg1_hash: sp_pkg1_hash {
|
||||
oid = SP_PKG1_HASH_OID;
|
||||
};
|
||||
sp_pkg2_hash: sp_pkg2_hash {
|
||||
oid = SP_PKG2_HASH_OID;
|
||||
};
|
||||
sp_pkg3_hash: sp_pkg3_hash {
|
||||
oid = SP_PKG3_HASH_OID;
|
||||
};
|
||||
sp_pkg4_hash: sp_pkg4_hash {
|
||||
oid = SP_PKG4_HASH_OID;
|
||||
};
|
||||
};
|
||||
|
||||
plat_sp_content_cert: plat_sp_content_cert {
|
||||
image-id = <PLAT_SP_CONTENT_CERT_ID>;
|
||||
signing-key = <&plat_pk>;
|
||||
antirollback-counter = <&non_trusted_nv_ctr>;
|
||||
|
||||
sp_pkg5_hash: sp_pkg5_hash {
|
||||
oid = SP_PKG5_HASH_OID;
|
||||
};
|
||||
sp_pkg6_hash: sp_pkg6_hash {
|
||||
oid = SP_PKG6_HASH_OID;
|
||||
};
|
||||
sp_pkg7_hash: sp_pkg7_hash {
|
||||
oid = SP_PKG7_HASH_OID;
|
||||
};
|
||||
sp_pkg8_hash: sp_pkg8_hash {
|
||||
oid = SP_PKG8_HASH_OID;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
images {
|
||||
compatible = "arm, img-descs";
|
||||
|
||||
hw_config {
|
||||
image-id = <HW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&hw_config_hash>;
|
||||
};
|
||||
|
||||
bl31_image {
|
||||
image-id = <BL31_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_hash>;
|
||||
};
|
||||
|
||||
soc_fw_config {
|
||||
image-id = <SOC_FW_CONFIG_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&soc_fw_config_hash>;
|
||||
};
|
||||
|
||||
rmm_image {
|
||||
image-id = <RMM_IMAGE_ID>;
|
||||
parent = <&cca_content_cert>;
|
||||
hash = <&rmm_hash>;
|
||||
};
|
||||
|
||||
bl32_image {
|
||||
image-id = <BL32_IMAGE_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_hash>;
|
||||
};
|
||||
|
||||
tos_fw_config {
|
||||
image-id = <TOS_FW_CONFIG_ID>;
|
||||
parent = <&trusted_os_fw_content_cert>;
|
||||
hash = <&tos_fw_config_hash>;
|
||||
};
|
||||
|
||||
bl33_image {
|
||||
image-id = <BL33_IMAGE_ID>;
|
||||
parent = <&non_trusted_fw_content_cert>;
|
||||
hash = <&nt_world_bl_hash>;
|
||||
};
|
||||
|
||||
nt_fw_config {
|
||||
image-id = <NT_FW_CONFIG_ID>;
|
||||
hash = <&nt_fw_config_hash>;
|
||||
};
|
||||
|
||||
#if defined(SPD_spmd)
|
||||
sp_pkg1 {
|
||||
image-id = <SP_PKG1_ID>;
|
||||
hash = <&sp_pkg1_hash>;
|
||||
};
|
||||
|
||||
sp_pkg2 {
|
||||
image-id = <SP_PKG2_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg2_hash>;
|
||||
};
|
||||
|
||||
sp_pkg3 {
|
||||
image-id = <SP_PKG3_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg3_hash>;
|
||||
};
|
||||
|
||||
sp_pkg4 {
|
||||
image-id = <SP_PKG4_ID>;
|
||||
parent = <&sip_sp_content_cert>;
|
||||
hash = <&sp_pkg4_hash>;
|
||||
};
|
||||
|
||||
sp_pkg5 {
|
||||
image-id = <SP_PKG5_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg5_hash>;
|
||||
};
|
||||
|
||||
sp_pkg6 {
|
||||
image-id = <SP_PKG6_ID>;
|
||||
parent = <&wrong_parent>;
|
||||
hash = <&sp_pkg6_hash>;
|
||||
};
|
||||
|
||||
sp_pkg7 {
|
||||
image-id = <SP_PKG7_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg7_hash>;
|
||||
};
|
||||
|
||||
sp_pkg8 {
|
||||
image-id = <SP_PKG8_ID>;
|
||||
parent = <&plat_sp_content_cert>;
|
||||
hash = <&sp_pkg8_hash>;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
non_volatile_counters: non_volatile_counters {
|
||||
compatible = "arm, non-volatile-counter";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cca_nv_ctr: cca_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = CCA_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
trusted_nv_ctr: trusted_nv_ctr {
|
||||
id = <TRUSTED_NV_CTR_ID>;
|
||||
oid = TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
|
||||
non_trusted_nv_ctr: non_trusted_nv_ctr {
|
||||
id = <NON_TRUSTED_NV_CTR_ID>;
|
||||
oid = NON_TRUSTED_FW_NVCOUNTER_OID;
|
||||
};
|
||||
};
|
||||
|
||||
rot_keys {
|
||||
swd_rot_pk: swd_rot_pk {
|
||||
oid = SWD_ROT_PK_OID;
|
||||
};
|
||||
prot_pk: prot_pk {
|
||||
oid = PROT_PK_OID;
|
||||
};
|
||||
};
|
33
tools/cot_dt2c/tests/test_util.py
Normal file
33
tools/cot_dt2c/tests/test_util.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cot_dt2c.cli import *
|
||||
from click.testing import CliRunner
|
||||
|
||||
def get_script_path():
|
||||
return os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
def test_convert():
|
||||
runner = CliRunner()
|
||||
test_file = get_script_path() + "/test.dtsi"
|
||||
test_output = get_script_path() + "/test.c"
|
||||
|
||||
result = runner.invoke(convert_to_c, [test_file, test_output])
|
||||
try:
|
||||
assert result.output == ""
|
||||
except:
|
||||
print("test convert fail")
|
||||
|
||||
try:
|
||||
os.remove(test_output)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if __name__=="__main__":
|
||||
test_convert()
|
Loading…
Add table
Reference in a new issue