diff --git a/Makefile b/Makefile index 64bccbcb0..cb88758f5 100644 --- a/Makefile +++ b/Makefile @@ -121,9 +121,6 @@ SPTOOL ?= ${SPTOOLPATH}/sptool.py SP_MK_GEN ?= ${SPTOOLPATH}/sp_mk_generator.py SP_DTS_LIST_FRAGMENT ?= ${BUILD_PLAT}/sp_list_fragment.dts -# Variables for use with Certificate Conversion (cot-dt2c) Tool -CERTCONVPATH ?= tools/cot_dt2c - # Variables for use with ROMLIB ROMLIBPATH ?= lib/romlib @@ -1619,7 +1616,6 @@ endif #(CHECKPATCH) clean: $(s)echo " CLEAN" $(call SHELL_REMOVE_DIR,${BUILD_PLAT}) - $(q)${MAKE} -C ${CERTCONVPATH} clean ifdef UNIX_MK $(q)${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean else @@ -1635,7 +1631,6 @@ realclean distclean: $(s)echo " REALCLEAN" $(call SHELL_REMOVE_DIR,${BUILD_BASE}) $(call SHELL_DELETE_ALL, ${CURDIR}/cscope.*) - $(q)${MAKE} -C ${CERTCONVPATH} clean ifdef UNIX_MK $(q)${MAKE} --no-print-directory -C ${FIPTOOLPATH} clean else diff --git a/changelog.yaml b/changelog.yaml index df0476f8e..dbbff992e 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -1450,6 +1450,9 @@ subsections: - title: Transfer List Compiler scope: tlc + - title: Chain of Trust device tree to C source file + scope: cot-dt2c + - title: Dependencies scope: deps diff --git a/docs/tools/cot-dt2c.rst b/docs/tools/cot-dt2c.rst index 7b7e56fde..e8bb1aced 100644 --- a/docs/tools/cot-dt2c.rst +++ b/docs/tools/cot-dt2c.rst @@ -13,23 +13,15 @@ Prerequisites #. Python (3.8 or later) #. `Poetry`_ Python package manager - Getting Started ~~~~~~~~~~~~~~~ -#. Install the tool - - .. code:: - - make install - - -#. Verify that the tool runs correctly - - .. code:: - - make test +``cot-dt2c`` is installed by default with TF-A's poetry environment. All of it's +dependencies are listed in `tools/cot_dt2c/pyproject.toml`_. +``cot-dt2c`` requires a standard DTS file without #ifdef, macros, or other +preprocessor directives. Therefore, you need to provide a preprocessed device +tree source(DTS) as input to the tool. #. Usage of the tool @@ -53,29 +45,6 @@ Getting Started visualize-cot validate-dt -#. Uninstall the tool - .. code:: - - make uninstall - - This command will uninstall the tool - - -#. Uninstall the tool and clean all the build file - .. code:: - - make clean - - This command will clean all the build file and implicitly uninstall the tool - - -#. Call the make file from TF-A root directory - .. code:: - - make -C tools/cot-dt2c install - make -C tools/cot-dt2c uninstall - make -C tools/cot-dt2c clean - Convert CoT descriptors to C file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -88,7 +57,7 @@ the validity of the CoT DT file. .. code:: - cot-dt2c convert-to-c [INPUT DTB PATH] [OUTPUT C PATH] + cot-dt2c convert-to-c [INPUT DTS PATH] [OUTPUT C PATH] cot-dt2c convert-to-c fdts/tbbr_cot_descriptors.dtsi test.c @@ -111,7 +80,7 @@ Currently the validation is specifically for checking the CoT DT file .. code:: - cot-dt2c validate-cot [INPUT DTB PATH] + cot-dt2c validate-cot [INPUT DTS PATH] cot-dt2c validate-cot fdts/tbbr_cot_descriptors.dtsi @@ -123,7 +92,7 @@ the certificates and the image of a CoT DT file. .. code:: - cot-dt2c visualize-cot [INPUT DTB PATH] + cot-dt2c visualize-cot [INPUT DTS PATH] cot-dt2c visualize-cot fdts/tbbr_cot_descriptors.dtsi @@ -146,4 +115,5 @@ is not installed along with the tool. *Copyright (c) 2024, Arm Limited. All rights reserved.* +.. _tools/cot_dt2c/pyproject.toml: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/integration/tools/cot_dt2c/pyproject.toml .. _Poetry: https://python-poetry.org/docs/ diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index dff0135bf..3c4ad647b 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -406,7 +406,6 @@ ifneq (${TRUSTED_BOARD_BOOT},0) COTDTPATH := fdts/tbbr_cot_descriptors.dtsi endif endif - bl2: cot-dt2c endif BL1_SOURCES += ${AUTH_SOURCES} \ @@ -481,16 +480,23 @@ ifeq (${TRANSFER_LIST}, 1) endif endif -cot-dt2c: ifneq ($(COTDTPATH),) - $(info COT CONVERSION FOR ${COTDTPATH}) - toolpath := $(shell which cot-dt2c) - ifeq (${toolpath},) - output := $(shell make -C ./${CERTCONVPATH} install) - $(info install output ${output}) - toolpath := $(shell which cot-dt2c) - endif - output := $(shell ${toolpath} convert-to-c ${COTDTPATH} ${BUILD_PLAT}/bl2_cot.c) - $(info ${output}) - BL2_SOURCES += ${BUILD_PLAT}/bl2_cot.c + cot-dt-defines = IMAGE_BL2 $(BL2_DEFINES) $(PLAT_BL_COMMON_DEFINES) + cot-dt-include-dirs = $(BL2_INCLUDE_DIRS) $(PLAT_BL_COMMON_INCLUDE_DIRS) + + cot-dt-cpp-flags = $(cot-dt-defines:%=-D%) + cot-dt-cpp-flags += $(cot-dt-include-dirs:%=-I%) + + cot-dt-cpp-flags += $(BL2_CPPFLAGS) $(PLAT_BL_COMMON_CPPFLAGS) + cot-dt-cpp-flags += $(CPPFLAGS) $(BL_CPPFLAGS) $(TF_CFLAGS_$(ARCH)) + cot-dt-cpp-flags += -c -x assembler-with-cpp -E -P -o $@ $< + + $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.dts): $(COTDTPATH) | $$(@D)/ + $(q)$($(ARCH)-cpp) $(cot-dt-cpp-flags) + + $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.c): $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.dts) | $$(@D)/ + $(q)poetry -q install + $(q)poetry run cot-dt2c convert-to-c $< $@ + + BL2_SOURCES += $(BUILD_PLAT)/$(COTDTPATH:.dtsi=.c) endif diff --git a/poetry.lock b/poetry.lock index b465f4858..9b98b182e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -216,6 +216,26 @@ files = [ [package.extras] test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +[[package]] +name = "cot-dt2c" +version = "0.1.0" +description = "CoT-dt2c Tool is a python script to convert CoT DT file into corresponding C file" +optional = false +python-versions = "^3.8" +files = [] +develop = true + +[package.dependencies] +click = "^8.1.7" +igraph = "^0.11.6" +plotly = "^5.23.0" +pydevicetree = "0.0.13" +pyparsing = "^3.1.2" + +[package.source] +type = "directory" +url = "tools/cot_dt2c" + [[package]] name = "docutils" version = "0.18.1" @@ -238,6 +258,66 @@ files = [ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] +[[package]] +name = "igraph" +version = "0.11.6" +description = "High performance graph data structures and algorithms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "igraph-0.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3f8b837181e8e87676be3873ce87cc92cc234efd58a2da2f6b4e050db150fcf4"}, + {file = "igraph-0.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:245c4b7d7657849eff80416f5df4525c8fc44c74a981ee4d44f0ef2612c3bada"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdb7be3d165073c0136295c0808e9edc57ba096cdb26e94086abb04561f7a292"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58974e20df2986a1ae52a16e51ecb387cc0cbeb41c5c0ddff4d373a1bbf1d9c5"}, + {file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bef14de5e8ab70724a43808b1ed14aaa6fe1002f87e592289027a3827a8f44a"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86c1e98de2e32d074df8510bf18abfa1f4c5fda4cb28a009985a5d746b0c0125"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ebc5b3d702158abeb2e4d2414374586a2b932e1a07e48352b470600e1733d528"}, + {file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0efe6d0fb22d3987a800eb3857ed04df9eb4c5dddd0998be05232cb646f1c337"}, + {file = "igraph-0.11.6-cp38-cp38-win32.whl", hash = "sha256:f4e68b27497b1c8ada2fb2bc35ef3fa7b0d72e84306b3d648d3de240fc618c32"}, + {file = "igraph-0.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:5665b33dfbfca5f54ce9b4fea6b97903bd0e99fb1b02acf5e57e600bdfa5a355"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:8aabef03d787b519d1075dfc0da4a1109fb113b941334883e3e7947ac30a459e"}, + {file = "igraph-0.11.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f2cc4a518d99cdf6cae514f85e93e56852bc8c325b3abb96037d1d690b5975f"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e859238be52ab8ccc614d18f9362942bc88ce543afc12548f81ae99b10801d"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d61fbe5e85eb4ae9efe08c461f9bdeedb02a2b5739fbc223d324a71f40a28be2"}, + {file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6620ba39df29fd42151becf82309b54e57148233c9c3ef890eed62e25eed8a5"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59666589bb3d07f310cda2c5106a8adeeb77c2ef27fecf1c6438b6091f4ca69d"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:8750b6d6caebf199cf7dc41c931f58e330153779707391e30f0a29f02666fb6e"}, + {file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:967d6f2c30fe94317da15e459374d0fb8ca3e56020412f201ecd07dd5b5352f2"}, + {file = "igraph-0.11.6-cp39-abi3-win32.whl", hash = "sha256:9744f95a67319eb6cb487ceabf30f5d7940de34bada51f0ba63adbd23e0f94ad"}, + {file = "igraph-0.11.6-cp39-abi3-win_amd64.whl", hash = "sha256:b80e69eb11faa9c57330a9ffebdde5808966efe1c1f638d4d4827ea04df7aca8"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0329c16092e2ea7930d5f8368666ce7cb704900cc0ea04e4afe9ea1dd46e44af"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21752313f449bd8688e5688e95ea7231cea5e9199c7162535029be0d9af848ac"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea25e136c6c4161f53ff58868b23ff6c845193050ab0e502236d68e5d4174e32"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac84433a03aef15e4b810010b08882b09854a3669450ccf31e392dbe295d2a66"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac697a44e3573169fa2b28c9c37dcf9cf01e0f558b845dd7123860d4c7c8fb89"}, + {file = "igraph-0.11.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdeae8bf35316eb1fb27bf667dcf5ecf5fcfb0b8f51831bc1b00c39c09c2d73b"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad7e4aa442935de72554b96733bf6d7f09eac5cee97988a2562bdd3ca173cfa3"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d2818780358a686178866d01568b9df1f29678581734ad7a78882bab54df004"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2352276a20d979f1dea360af4202bb9f0c9a7d2c77f51815c0e625165e82013d"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:687fdab543b507d622fa3043f4227e5b26dc61dcf8ff8c0919fccddcc655f8b8"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f7f8214cd48c9a4d97f7346a4152ba2d4ac95fb5ee0df4ecf224fce4ba3d14"}, + {file = "igraph-0.11.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2b9cc69ede53f76ffae03b066609aa90184dd68ef15da8c104a97cebb9210838"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:591e1e447c3f0092daf7613a3eaedab83f9a0b0adbaf7702724c5117ded038a5"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ca558eb331bc687bc33e5cd23717e22676e9412f8cda3a31d30c996a0487610d"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf43c30e08debb087c9e3da69aa5cf1b6732968da34d55a614e3421b9a452146"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d38e8d7db72b187d9d2211d0d06b3271fa9f32b04d49d789e2859b5480db0d0"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a318b059051ff78144a1c3cb880f4d933c812bcdb3d833a49cd7168d0427672"}, + {file = "igraph-0.11.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c54027add809b3c5b6685b8deca4ea4763fd000b9ea45c7ee46b7c9d61ff15e"}, + {file = "igraph-0.11.6.tar.gz", hash = "sha256:837f233256c3319f2a35a6a80d94eafe47b43791ef4c6f9e9871061341ac8e28"}, +] + +[package.dependencies] +texttable = ">=1.6.2" + +[package.extras] +cairo = ["cairocffi (>=1.2.0)"] +doc = ["Sphinx (>=7.0.0)", "pydoctor (>=23.4.0)", "sphinx-gallery (>=0.14.0)", "sphinx-rtd-theme (>=1.3.0)"] +matplotlib = ["matplotlib (>=3.6.0)"] +plotly = ["plotly (>=5.3.0)"] +plotting = ["cairocffi (>=1.2.0)"] +test = ["Pillow (>=9)", "cairocffi (>=1.2.0)", "matplotlib (>=3.6.0)", "networkx (>=2.5)", "numpy (>=1.19.0)", "pandas (>=1.1.0)", "plotly (>=5.3.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "scipy (>=1.5.0)"] +test-musl = ["cairocffi (>=1.2.0)", "networkx (>=2.5)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] + [[package]] name = "imagesize" version = "1.4.1" @@ -479,6 +559,21 @@ wheel = "*" coverage = ["covdefaults", "pytest-cov"] testing = ["flit-core (>=2,<4)", "poetry-core (>=1.0.0)", "pytest (>=7.2.0)", "pytest-rerunfailures", "pytest-xdist", "tomli-w"] +[[package]] +name = "plotly" +version = "5.23.0" +description = "An open-source, interactive data visualization library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "plotly-5.23.0-py3-none-any.whl", hash = "sha256:76cbe78f75eddc10c56f5a4ee3e7ccaade7c0a57465546f02098c0caed6c2d1a"}, + {file = "plotly-5.23.0.tar.gz", hash = "sha256:89e57d003a116303a34de6700862391367dd564222ab71f8531df70279fc0193"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + [[package]] name = "prettytable" version = "3.10.2" @@ -496,6 +591,20 @@ wcwidth = "*" [package.extras] tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] +[[package]] +name = "pydevicetree" +version = "0.0.13" +description = "A library for parsing Devicetree Source v1" +optional = false +python-versions = ">=3.5" +files = [ + {file = "pydevicetree-0.0.13-py3-none-any.whl", hash = "sha256:d61c695cec925b90a8b5740053f4b604e51154a9b36e62a2f12ed9ceaf2f8c38"}, + {file = "pydevicetree-0.0.13.tar.gz", hash = "sha256:5700c05df89bad8fd729c11aa6f764a3323bcb3796f13b32481ae34445cfc1b7"}, +] + +[package.dependencies] +pyparsing = "*" + [[package]] name = "pyelftools" version = "0.29" @@ -521,6 +630,20 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pyproject-hooks" version = "1.1.0" @@ -882,6 +1005,32 @@ Sphinx = ">=1.6.3" [package.extras] cairosvg = ["cairosvg (>=1.0)"] +[[package]] +name = "tenacity" +version = "9.0.0" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "texttable" +version = "1.7.0" +description = "module to create simple ASCII tables" +optional = false +python-versions = "*" +files = [ + {file = "texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917"}, + {file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, +] + [[package]] name = "tlc" version = "0.9.0" @@ -1005,4 +1154,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8798a2d1efd456c3b68ae464a216f015afaa1bbb653a905148bef17ab8ce278e" +content-hash = "d893034cad02533bc86fb98c7d93a0eac6a755fea5efd553924e4762ed3f1fdb" diff --git a/pyproject.toml b/pyproject.toml index 03d898e4d..f0b392541 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ memory = "memory.memmap:main" [tool.poetry.dependencies] python = "^3.8" +cot-dt2c = {path = "tools/cot_dt2c", develop = true} tlc = {path = "tools/tlc"} [tool.poetry.group.docs] diff --git a/tools/cot_dt2c/.gitignore b/tools/cot_dt2c/.gitignore new file mode 100644 index 000000000..ad4a1f17f --- /dev/null +++ b/tools/cot_dt2c/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/tools/cot_dt2c/Makefile b/tools/cot_dt2c/Makefile deleted file mode 100644 index ad8d9f5e9..000000000 --- a/tools/cot_dt2c/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# 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/ diff --git a/tools/cot_dt2c/cot_dt2c/LICENSE b/tools/cot_dt2c/cot_dt2c/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/tools/cot_dt2c/cot_dt2c/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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. diff --git a/tools/cot_dt2c/cot_dt2c/cot_parser.py b/tools/cot_dt2c/cot_dt2c/cot_parser.py index c1d53e211..39e51db3c 100644 --- a/tools/cot_dt2c/cot_dt2c/cot_parser.py +++ b/tools/cot_dt2c/cot_dt2c/cot_parser.py @@ -6,35 +6,15 @@ 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 pydevicetree.ast import CellArray, LabelReference +from pydevicetree import Devicetree, Property, Node 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 +from typing import List, Optional 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) + self.tree = Devicetree.parseFile(inputfile) except: print("not a valid CoT DT file") exit(1) @@ -336,13 +316,10 @@ class COT: 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" + ptr = type_desc + "_buf" + len = "HASH_DER_LEN" + if re.search("_pk$", type_desc): + len = "PK_DER_LEN" # edge case if not self.if_root(cert) and "key_cert" in cert.name: @@ -351,7 +328,7 @@ class COT: return type_desc, ptr, len - def get_node(self, nodes: list[Node], name: str) -> Node: + def get_node(self, nodes: List[Node], name: str) -> Node: for i in nodes: if i.name == name: return i @@ -458,10 +435,6 @@ class COT: 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() @@ -478,71 +451,16 @@ class COT: 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): + def include_to_c(self, f): f.write("#include \n") f.write("#include \n") f.write("#include \n") f.write("#include \n") - f.write("\n") - for i in include: - f.write(i) - f.write("\n") f.write("#include \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 generate_header(self, output): + self.include_to_c(output) def all_cert_to_c(self, f): certs = self.get_all_certificates() @@ -552,17 +470,16 @@ class COT: 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)) + node_image_id: int = node.get_field("image-id") - 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(f"static const auth_img_desc_t {node.name} = {{\n") + f.write(f"\t.img_id = {node_image_id},\n") 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)) + node_parent: Node = node.get_field("parent") + + f.write(f"\t.parent = &{node_parent.label.name},\n") else: f.write("\t.parent = NULL,\n") @@ -608,13 +525,9 @@ class COT: 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.ptr = (void *){},\n".format(ptr)) - f.write("\t\t\t\t.len = {}\n".format(data_len)) + f.write("\t\t\t\t.len = (unsigned int){}\n".format(data_len)) f.write("\t\t\t}\n") f.write("\t\t}}{}\n".format("," if i != len(auth_data) - 1 else "")) @@ -623,42 +536,31 @@ class COT: 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)) + node_image_id: int = node.get_field("image-id") + node_parent: Node = node.get_field("parent") + node_hash: Node = node.get_field("hash") - 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(f"static const auth_img_desc_t {node.name} = {{\n") + f.write(f"\t.img_id = {node_image_id},\n") f.write("\t.img_type = IMG_RAW,\n") - f.write("\t.parent = &{},\n".format(node.get_field("parent").label.name)) + f.write(f"\t.parent = &{node_parent.label.name},\n") 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(f"\t\t\t\t.hash = &{node_hash.label.name}\n") 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): @@ -672,7 +574,10 @@ class COT: 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"))) + nv_oid: str = nv.get_field("oid") + + f.write(f"static auth_param_type_desc_t {nv.name} = "\ + f"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_NV_CTR, \"{nv_oid}\");\n") f.write("\n") @@ -682,7 +587,10 @@ class COT: 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"))) + pk_oid: str = p.get_field("oid") + + f.write(f"static auth_param_type_desc_t {p.name} = "\ + f"AUTH_PARAM_TYPE_DESC(AUTH_PARAM_PUB_KEY, \"{pk_oid}\");\n") f.write("\n") return @@ -690,30 +598,17 @@ class COT: def buf_to_c(self, f): certs = self.get_all_certificates() - buffers = {} + buffers = set() 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") + if not ptr in buffers: + f.write(f"static unsigned char {ptr}[{data_len}];\n") + buffers.add(ptr) f.write("\n") @@ -726,29 +621,18 @@ class COT: 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 re.search("_pk$", name): + ty = "AUTH_PARAM_PUB_KEY" + elif re.search("_hash$", name): + ty = "AUTH_PARAM_HASH" - if ifdef: - for i in ifdef: - f.write("#endif\n") + f.write(f"static auth_param_type_desc_t {name} = "\ + f"AUTH_PARAM_TYPE_DESC({ty}, \"{oid}\");\n") f.write("\n") @@ -759,28 +643,14 @@ class COT: 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, ",")) + c_image_id: int = c.get_field("image-id") - if ifdef: - for i in ifdef: - f.write("#endif\n") + f.write(f"\t[{c_image_id}] = &{c.name},\n") for i, c in enumerate(images): - ifdef = c.get_fields("ifdef") - if ifdef: - for i in ifdef: - f.write("{}\n".format(i)) + c_image_id: int = c.get_field("image-id") - 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(f"\t[{c_image_id}] = &{c.name},\n") f.write("};\n\n") f.write("REGISTER_COT(cot_desc);\n") @@ -789,16 +659,15 @@ class COT: 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) + + with open(self.output, 'w+') as output: + self.generate_header(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 diff --git a/tools/cot_dt2c/cot_dt2c/dt_validator.py b/tools/cot_dt2c/cot_dt2c/dt_validator.py index 65e8ca231..ade037ca4 100644 --- a/tools/cot_dt2c/cot_dt2c/dt_validator.py +++ b/tools/cot_dt2c/cot_dt2c/dt_validator.py @@ -7,7 +7,7 @@ import sys from os import path, walk, mkdir import subprocess -from cot_dt2c.pydevicetree import * +from pydevicetree import Devicetree class bcolors: HEADER = '\033[95m' diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py deleted file mode 100644 index 49595a71c..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/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 diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py deleted file mode 100644 index f30d89791..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py deleted file mode 100644 index fdd6f0e15..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/directive.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/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 "" % 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) diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py deleted file mode 100644 index 30c54dc20..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/helpers.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/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 diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py deleted file mode 100644 index d203af870..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/node.py +++ /dev/null @@ -1,514 +0,0 @@ -#!/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 "" % (self.name, self.address) - return "" % 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 "" % 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 "" % 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 diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py deleted file mode 100644 index d5fb687a5..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/property.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/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 "" - - 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 "" - - 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 "" - - 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 "" - - 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 "" - - 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 "" - - 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 "" % 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) diff --git a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py b/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py deleted file mode 100644 index 54b2d28c8..000000000 --- a/tools/cot_dt2c/cot_dt2c/pydevicetree/ast/reference.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/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 "