From 5e999cfcf7d28072aa280300b15506f2e5876e71 Mon Sep 17 00:00:00 2001 From: Maria Wisniewska Date: Mon, 29 Mar 2021 21:54:25 +0200 Subject: [PATCH] Release 1.3.1 --- .gitignore | 2 + README.md | 13 +- codecheck.cmd | 2 +- docs/api/dat.rst | 8 - docs/api/mboot_interfaces.rst | 2 +- docs/api/sdp_interfaces.rst | 2 +- docs/api/shadowregs.rst | 11 + docs/index.rst | 1 + examples/dat/README.md | 2 +- release_notes.txt | 77 +- requirements.txt | 3 +- setup.py | 13 +- spsdk/__init__.py | 8 +- spsdk/__version__.py | 2 +- spsdk/apps/README.md | 2 +- spsdk/apps/blhost.py | 47 +- spsdk/apps/blhost_helper.py | 22 +- spsdk/apps/nxpdebugmbox.py | 1 + spsdk/apps/pfr.py | 249 +++--- spsdk/apps/pfrc.py | 35 +- spsdk/apps/shadowregs.py | 70 +- spsdk/apps/spsdk_apps.py | 4 +- spsdk/apps/utils.py | 7 +- spsdk/dat/__init__.py | 1 - spsdk/dat/debug_mailbox.py | 4 +- spsdk/dat/dm_commands.py | 2 +- spsdk/data/pfr/cfpa/database.json | 49 +- spsdk/data/pfr/cfpa/niobe4_a1.xml | 28 +- spsdk/data/pfr/cfpa/niobe4mini_a1.xml | 52 +- spsdk/data/pfr/cfpa/niobe4nano_a1.xml | 52 +- spsdk/data/pfr/cmpa/database.json | 27 +- spsdk/data/pfr/cmpa/niobe4_a1.xml | 33 +- spsdk/data/pfr/cmpa/niobe4analog_a1.xml | 4 +- spsdk/data/pfr/cmpa/niobe4mini_a1.xml | 57 +- spsdk/data/pfr/cmpa/niobe4nano_a1.xml | 52 +- spsdk/data/pfr/rules.json | 4 +- .../regs_desc_template.html} | 9 +- spsdk/data/shadow_regs/database.json | 32 - spsdk/data/shadow_regs/imxrt595_b0.xml | 337 -------- spsdk/data/shadowregs/database.json | 50 ++ spsdk/data/shadowregs/imxrt595_b0.xml | 354 ++++++++ .../imxrt685_b0.xml | 319 +++---- spsdk/debuggers/debug_probe.py | 6 +- spsdk/debuggers/debug_probe_jlink.py | 16 +- spsdk/debuggers/debug_probe_pemicro.py | 14 +- spsdk/debuggers/debug_probe_pyocd.py | 11 +- spsdk/debuggers/utils.py | 36 +- spsdk/image/mbimg.py | 18 +- spsdk/image/misc.py | 26 +- spsdk/image/trustzone.py | 5 +- spsdk/mboot/commands.py | 28 +- spsdk/mboot/error_codes.py | 2 +- spsdk/mboot/interfaces/uart.py | 12 +- spsdk/mboot/interfaces/usb.py | 4 + spsdk/mboot/mcuboot.py | 7 +- spsdk/pfr/__init__.py | 12 +- spsdk/pfr/exceptions.py | 21 + spsdk/pfr/pfr.py | 605 ++++++++----- spsdk/pfr/processor.py | 10 +- spsdk/pfr/translator.py | 47 +- spsdk/sbfile/commands.py | 48 +- spsdk/sbfile/images.py | 1 + spsdk/sdp/interfaces/uart.py | 7 +- spsdk/shadowregs/__init__.py | 9 + .../shadowregs.py} | 269 +++--- spsdk/utils/__init__.py | 16 +- spsdk/utils/exceptions.py | 24 + spsdk/utils/misc.py | 163 +++- spsdk/utils/reg_config.py | 219 +++++ spsdk/utils/registers.py | 689 +++++++++++---- tests/dat/test_nxpdebugmbox.py | 2 +- tests/debuggers/conftest.py | 2 +- tests/debuggers/debug_probe_virtual.py | 22 +- tests/debuggers/test_debug_probe virtual.py | 156 ---- tests/debuggers/test_debug_probe.py | 3 + tests/debuggers/test_debug_probe_utils.py | 29 +- tests/debuggers/test_debug_virtual_probe.py | 167 ++++ tests/image/misc/test_dict_diff.py | 16 +- tests/image/misc/test_format_value.py | 16 +- tests/mboot/blhost/test_blhost_cli.py | 16 + tests/mboot/blhost/test_blhost_utils.py | 15 +- tests/mboot/test_mboot_api.py | 12 +- .../app_ram_iar_crc_keystore.sb.txt | 6 +- .../debug_logs/app_ram_iar_crc_otp.sb.txt | 6 +- .../app_ram_iar_encr_keystore_keystore.sb.txt | 6 +- ...pp_ram_iar_signed_keystore_keystore.sb.txt | 6 +- .../app_ram_iar_unsigned_keystore.sb.txt | 6 +- ...app_ram_iar_unsigned_otfad_keystore.sb.txt | 6 +- .../app_ram_iar_unsigned_otfad_otp.sb.txt | 6 +- .../app_ram_iar_unsigned_otp.sb.txt | 6 +- .../app_xip_iar_signed_otfad_keystore.sb.txt | 6 +- .../app_xip_iar_signed_otfad_otp.sb.txt | 6 +- .../app_xip_mcux_unsigned_keystore.sb.txt | 6 +- .../app_xip_mcux_unsigned_otp.sb.txt | 6 +- .../rt5xx/output/app_ram_iar_crc_keystore.sb | Bin 15904 -> 15904 bytes .../data/rt5xx/output/app_ram_iar_crc_otp.sb | Bin 15920 -> 15920 bytes .../app_ram_iar_encr_keystore_keystore.sb | Bin 18640 -> 18640 bytes .../app_ram_iar_signed_keystore_keystore.sb | Bin 18560 -> 18560 bytes .../output/app_ram_iar_unsigned_keystore.sb | Bin 15904 -> 15904 bytes .../app_ram_iar_unsigned_otfad_keystore.sb | Bin 17616 -> 17616 bytes .../output/app_ram_iar_unsigned_otfad_otp.sb | Bin 17616 -> 17616 bytes .../rt5xx/output/app_ram_iar_unsigned_otp.sb | Bin 15920 -> 15920 bytes .../app_xip_iar_signed_otfad_keystore.sb | Bin 19152 -> 19152 bytes .../output/app_xip_iar_signed_otfad_otp.sb | Bin 19152 -> 19152 bytes .../output/app_xip_mcux_unsigned_keystore.sb | Bin 21040 -> 21040 bytes .../rt5xx/output/app_xip_mcux_unsigned_otp.sb | Bin 21056 -> 21056 bytes tests/mcu_examples/test_rt10xx.py | 9 +- tests/mcu_examples/test_rt5xx.py | 10 +- tests/pfr/data/CMPA_96MHz.bin | Bin 512 -> 512 bytes tests/pfr/data/bad_dev.yml | 13 + tests/pfr/data/bad_rev.yml | 13 + tests/pfr/data/cfpa_pfrc.json | 6 +- tests/pfr/data/cfpa_test.yml | 25 + tests/pfr/data/cmpa_96mhz.json | 6 +- tests/pfr/data/cmpa_96mhz.yml | 87 ++ tests/pfr/data/cmpa_96mhz_rotkh.yml | 87 ++ tests/pfr/data/cmpa_pfrc.json | 6 +- tests/pfr/data/empty_file | 0 tests/pfr/data/empty_json.json | 3 + tests/pfr/data/empty_json1.json | 3 + tests/pfr/data/empty_yml.yml | 1 + tests/pfr/data/rules.json | 12 +- tests/pfr/test_bitarray.py | 35 +- tests/pfr/test_cli_utils.py | 31 +- tests/pfr/test_pfr.py | 192 ++++- tests/pfr/test_pfr_cli.py | 38 +- tests/pfr/test_pfrc_cli.py | 5 +- tests/sbfile/test_commands_api.py | 76 +- tests/shadowregs/conftest.py | 3 +- tests/shadowregs/data/sh_test_dev.json | 2 +- tests/shadowregs/data/test_database.json | 4 +- .../data/test_database_invalid_computed.json | 2 +- tests/shadowregs/test_registers.py | 461 ---------- tests/shadowregs/test_shadowregs.py | 65 +- tests/shadowregs/test_shadowregs_app.py | 17 +- tests/utils/crypto/test_common.py | 12 +- tests/utils/data/bad_format.xml | 5 + tests/utils/data/group_none_reg.yml | 12 + tests/utils/data/group_reg.yml | 3 + tests/utils/data/grp_regs.xml | 13 + .../data/reg_config.json | 35 +- .../{shadowregs => utils}/data/registers.xml | 2 +- .../data/registers_corr.xml | 2 +- tests/utils/test_misc.py | 158 +++- tests/utils/test_registers.py | 808 ++++++++++++++++++ tools/gitcov-defaults.ini | 6 +- tools/gitcov.py | 176 +++- tools/sr_xls2xml.py | 326 +++---- tools/test_debuggers.py | 87 -- tox.ini | 1 - 150 files changed, 4891 insertions(+), 2869 deletions(-) create mode 100644 docs/api/shadowregs.rst rename spsdk/data/{pfr/pfr_desc_template.html => regs/regs_desc_template.html} (88%) delete mode 100644 spsdk/data/shadow_regs/database.json delete mode 100644 spsdk/data/shadow_regs/imxrt595_b0.xml create mode 100644 spsdk/data/shadowregs/database.json create mode 100644 spsdk/data/shadowregs/imxrt595_b0.xml rename spsdk/data/{shadow_regs => shadowregs}/imxrt685_b0.xml (56%) create mode 100644 spsdk/pfr/exceptions.py create mode 100644 spsdk/shadowregs/__init__.py rename spsdk/{dat/shadow_regs.py => shadowregs/shadowregs.py} (56%) create mode 100644 spsdk/utils/exceptions.py create mode 100644 spsdk/utils/reg_config.py delete mode 100644 tests/debuggers/test_debug_probe virtual.py create mode 100644 tests/debuggers/test_debug_virtual_probe.py create mode 100644 tests/pfr/data/bad_dev.yml create mode 100644 tests/pfr/data/bad_rev.yml create mode 100644 tests/pfr/data/cfpa_test.yml create mode 100644 tests/pfr/data/cmpa_96mhz.yml create mode 100644 tests/pfr/data/cmpa_96mhz_rotkh.yml create mode 100644 tests/pfr/data/empty_file create mode 100644 tests/pfr/data/empty_json.json create mode 100644 tests/pfr/data/empty_json1.json create mode 100644 tests/pfr/data/empty_yml.yml delete mode 100644 tests/shadowregs/test_registers.py create mode 100644 tests/utils/data/bad_format.xml create mode 100644 tests/utils/data/group_none_reg.yml create mode 100644 tests/utils/data/group_reg.yml create mode 100644 tests/utils/data/grp_regs.xml rename tests/{shadowregs => utils}/data/reg_config.json (59%) rename tests/{shadowregs => utils}/data/registers.xml (98%) rename tests/{shadowregs => utils}/data/registers_corr.xml (98%) create mode 100644 tests/utils/test_registers.py delete mode 100644 tools/test_debuggers.py diff --git a/.gitignore b/.gitignore index 30365d08..e8813167 100644 --- a/.gitignore +++ b/.gitignore @@ -102,7 +102,9 @@ ENV/ env.bak/ venv.bak/ env* +*env/ venv* +*venv/ # Spyder project settings .spyderproject diff --git a/README.md b/README.md index feccf42d..ad33f0fe 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ - - -[doc-location]: https://spsdk.readthedocs.io -[pypi-location]: https://pypi.org/project/spsdk - - # NXP Secure Provisioning SDK **Secure Provisioning SDK (SPSDK)** enables connection and communication with target devices for purposes of secure provisioning and programming. Delivered as python library with command-line applications for direct utilization. @@ -12,9 +6,9 @@ ## Links -* [PyPI][pypi-location] +* [PyPi](https://pypi.org/project/spsdk/) * [Release Notes](release_notes.txt) -* [Documentation][doc-location] +* [Documentation](https://spsdk.readthedocs.io) ## Supported Devices @@ -59,7 +53,8 @@ Following NXP devices are supported: ``` > In **Windows OS** you need to install [Microsoft Visual C++ Build Tools](https://www.scivision.dev/python-windows-visual-c-14-required/) - Note: If you use pip version 20.3, please downgrade it to 20.2.4, because of the new resolver functionality. + Note: In case of problems during instalation, please make sure that you have the latest pip version. + You can upgrade pip using this command: 'pip install --upgrade pip'. ## Usage diff --git a/codecheck.cmd b/codecheck.cmd index 86716141..efea85cf 100644 --- a/codecheck.cmd +++ b/codecheck.cmd @@ -43,5 +43,5 @@ radon cc --min D spsdk > %CD%\reports\radonD.txt radon cc --min C spsdk > %CD%\reports\radonC.txt @rem ------------------------------------------------------- @rem gitcov (coverage of changed files) -python tools\gitcov.py --verbose --coverage-report reports\coverage.xml +python tools\gitcov.py --coverage-report reports\coverage.xml @if %errorlevel% gtr 0 echo "<<<### GIT-COV ERROR DETECTED #################################################>>>" diff --git a/docs/api/dat.rst b/docs/api/dat.rst index ceebcf2f..77fe5224 100644 --- a/docs/api/dat.rst +++ b/docs/api/dat.rst @@ -57,11 +57,3 @@ Module with common utils for DAT module :members: :undoc-members: :show-inheritance: - - -Module with the shadow registers control DAT support file ----------------------------------------------------------- -.. automodule:: spsdk.dat.shadow_regs - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/mboot_interfaces.rst b/docs/api/mboot_interfaces.rst index 2bdf4ac9..d483d6b6 100644 --- a/docs/api/mboot_interfaces.rst +++ b/docs/api/mboot_interfaces.rst @@ -27,4 +27,4 @@ MBoot Interface Class .. automodule:: spsdk.mboot.interfaces.base :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/api/sdp_interfaces.rst b/docs/api/sdp_interfaces.rst index 309cb24d..8274edd5 100644 --- a/docs/api/sdp_interfaces.rst +++ b/docs/api/sdp_interfaces.rst @@ -28,4 +28,4 @@ SDP Interface Class .. automodule:: spsdk.sdp.interfaces.base :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/docs/api/shadowregs.rst b/docs/api/shadowregs.rst new file mode 100644 index 00000000..a7580bf1 --- /dev/null +++ b/docs/api/shadowregs.rst @@ -0,0 +1,11 @@ +Shadow Registers API +===================== + +This module contains support for Shadow Registers. + +Module with the shadow registers control DAT support file +---------------------------------------------------------- +.. automodule:: spsdk.shadowregs.shadowregs + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 456b176b..e0f5e036 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,6 +19,7 @@ api/image api/mboot api/pfr + api/shadowregs api/sbfile api/sdp api/utils diff --git a/examples/dat/README.md b/examples/dat/README.md index 4f431f96..e6d99f98 100644 --- a/examples/dat/README.md +++ b/examples/dat/README.md @@ -34,4 +34,4 @@ Content of the `hsm` represents the remote side of things 3) generate the Debug Credential file - `nxpkeygen gendc --config dck_rsa_2048.yml --plugin hsm/sasp.py my.dc` - you may need to add the `--force` flag if you are running the example multiple times -4) for comparison, you may try to use signing a local file, to do so, comment out line 11 in yaml file and uncomment line 14 or 15 (the have the same effect) +4) for comparison, you may try to use signing a local file, to do so, comment out line 11 in yaml file and uncomment line 14 or 15 (the have the same effect) \ No newline at end of file diff --git a/release_notes.txt b/release_notes.txt index 2f3857ae..62a4d9a0 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,16 +1,51 @@ Release Notes for Secure Provisioning SDK ========================================== -Version: 1.3 -Date: 05-March-2021 +Secure Provisioning SDK (SPSDK) is a unified, reliable, and easy-to-use SW library. It targets a wide +portfolio of NXP MCUs, providing a solid foundation from quick customer prototyping up to +production deployment. The library allows the user to connect and communicate with a device, +configure the device, prepare, download and upload data, including security operations. It is +delivered in the form of a python library and command-line applications. -Secure Provisioning SDK (SPSDK) is a unified, reliable, and easy to use SW library working across the NXP MCU portfolio -providing a strong foundation from quick customer prototyping up to production deployment. The library allows the user -to connect and communicate with a device, configure the device, prepare, download and upload data including security -operations. It is delivered in a form of a python library and command-line applications. +Version: 1.3.1. +Date: 29-March-2021 Features: +- [PFR] configuration template supports YAML with description, backward compatibility with JSON ensured +- [PFR] API change: "keys" parameter has been moved from __init__ to export +- [PRF] sub-commands renamed: (user-config -> get-cfg-template; parse -> parse-binary; generate -> generate-binary) +- [blhost] allow key names for key-provisioning commands +- [blhost] support for RT1170, RT1160 +- Shadow Registers tool is now top-level module + +Bugfixes: +- [blhost] fix baud rate parameter +- [PFR] fix in data for Niob4, Niobe4 Mini, Niobe4 Nano +- bug fixes for sb 2.1 commands and options + + +Supported devices: +================== +- LPC55S6x, LPC55S2x (Niobe4) +- LPC55S0x (Niobe 4 Nano) +- LPC55S1x (Niobe4 Mini) +- i.MX RT105x, RT106x +- i.MX RT595S, RT685S +- i.MX RT1160(blhost), RT1170(blhost) + + +System Requirements: +=================== +Operating System: +- Windows +- Mac OS +- Linux + + +Revision History: +================ +1.3 - support creation of SB version 3.1 (for N4Analog) -- elftosb application based on legacy elf2sb supporting SB 3.1 support +- elftosb application based on legacy elf2sb supporting SB 3.1 support - nxpdevscan - application for connected USB, UART devices discovery - shadowregs - application for shadow registers management using DebugProbe - support USB path argument in blhost/sdphost (all supported OS) @@ -29,26 +64,7 @@ Features: - add key selector option to generate-key-blob command - add nolock/lock selector to efuse-program-once command - add hexdata option to write-memory command - -Supported devices: -================== -- LPC55S6x, LPC55S2x (Niobe4) -- LPC55S0x (Niobe 4 Nano) -- LPC55S1x (Niobe4 Mini) -- i.MX RT105x, RT106x -- i.MX RT595S, RT685S - - -System Requirements: -=================== -Operating System: -- Windows -- Mac OS -- Linux - -Revision History: -================ 1.2 - support for Niobe4 Analog devices - extend support for Niobe4 Mini, Nano @@ -75,12 +91,13 @@ Revision History: - support for i.MX RT105x and RT106x devices - support for i.MX RT595S and RT685S devices - connectivity to the target via UART, USB-HID. -- support for generating, saving, loading RSA keys with different sizes -- generation and management of certificate +- support for generating, saving, loading of RSA keys with different sizes +- genaration and management of certificate - CLI utility blhost for communication with boot loader on a target - CLI utility sdphost for communication with ROM on a target -- CLI utility PFR for generating and parsing Protected Flash Regions - CMPA and CFPA regions +- CLI utility PFR for generating and parsing Protocted Flash Regions - CMPA and CFPA regions + -License: +Licence: ========= BSD-3 License diff --git a/requirements.txt b/requirements.txt index f9fd5e79..afe0bf85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,10 @@ commentjson>=0.9,<1 -jmespath<=0.10.0 astunparse>=1.6,<2 bitstring>=3.1,<3.2 click>=7.0,<8 click-option-group>=0.3.0,<0.6 construct~=2.10 -crccheck>=0.6,<2 +crcmod==1.7 hexdump~=3.3 asn1crypto>=1.2,<2 cryptography>=3.3,<3.4.4 diff --git a/setup.py b/setup.py index f7eec086..58113a98 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,6 @@ import sys from typing import List -import pip # type: ignore - from setuptools import setup, find_packages # type: ignore @@ -24,12 +22,6 @@ def sanitize_version(version_str: str) -> str: return sanitizer[len(version_str)] -if sys.version_info >= (3, 8, 0) and sanitize_version(pip.__version__) < '19.2.3': - print(f"With python {sys.version}, you're using an old version of pip: {pip.__version__}") - print("Please update pip using: 'python -m pip install --upgrade pip'.") - sys.exit(1) - - with open('requirements.txt') as req_file: requirements = req_file.read().splitlines() # avoid build errors on readthedocs (excluding hidapi, which depends on C module) @@ -51,6 +43,11 @@ def sanitize_version(version_str: str) -> str: version=version_info["__version__"], description='Open Source Secure Provisioning SDK for NXP MCU/MPU', url="https://github.com/NXPmicro/spsdk", + project_urls={ + "Code": 'https://github.com/NXPmicro/spsdk', + "Issue tracker": 'https://github.com/NXPmicro/spsdk/issues', + "Documentation": 'https://spsdk.readthedocs.io', + }, author="NXP", author_email="michal.starecek@nxp.com", license='BSD-3-Clause', diff --git a/spsdk/__init__.py b/spsdk/__init__.py index 6b4783d4..1aa56b5e 100644 --- a/spsdk/__init__.py +++ b/spsdk/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2020 NXP +# Copyright 2019-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -22,10 +22,12 @@ from .__version__ import __version__ as version from .exceptions import SPSDKError -SPSDK_DATA_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") - __author__ = "NXP" __contact__ = "michal.starecek@nxp.com" __license__ = "BSD-3-Clause" __version__ = version __release__ = "alpha" + +# The SPSDK behavior settings +SPSDK_DATA_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") +SPSDK_YML_INDENT = 2 diff --git a/spsdk/__version__.py b/spsdk/__version__.py index 701c0a00..437fbc5d 100644 --- a/spsdk/__version__.py +++ b/spsdk/__version__.py @@ -10,4 +10,4 @@ Having the version in a separate file makes it easier to share it with setup.py """ -__version__ = "1.3.0" +__version__ = "1.3.1" diff --git a/spsdk/apps/README.md b/spsdk/apps/README.md index 1948cbdc..8c2044d7 100644 --- a/spsdk/apps/README.md +++ b/spsdk/apps/README.md @@ -7,7 +7,7 @@ After installing SPSDK, several applications are present directly on PATH as exe - [elftosb](elftosb.py) - utility for generating TrustZone, MasterBootImage and SecureBinary images. - [nxpcertgen](nxpcertgen.py) - utility for generating the self-signed x.509 certificate. - [nxpdebugmbox](nxpdebugmbox.py) - utility for performing the Debug Authentication. -- [nxpdevscan](nxpdscan.py) - utility for listing all connected NXP USB and UART devices. +- [nxpdevscan](nxpdevscan.py) - utility for listing all connected NXP USB and UART devices. - [nxpkeygen](nxpkeygen.py) - utility for generating RSA/ECC key pairs and debug credential files based on YAML configuration file. - [pfr](pfr.py) - simple utility for creation and analysis of protected regions - CMPA and CFPA. - [pfrc](pfrc.py) - simple utility for search of brick-conditions in PFR settings. diff --git a/spsdk/apps/blhost.py b/spsdk/apps/blhost.py index c60a565d..ec42e680 100644 --- a/spsdk/apps/blhost.py +++ b/spsdk/apps/blhost.py @@ -16,7 +16,7 @@ from click_option_group import MutuallyExclusiveOptionGroup, optgroup from spsdk import __version__ as spsdk_version, SPSDKError -from spsdk.apps.blhost_helper import parse_property_tag +from spsdk.apps.blhost_helper import parse_key_prov_key_type, parse_property_tag from spsdk.apps.utils import ( INT, get_interface, format_raw_data, catch_spsdk_error, parse_file_and_size, parse_hex_data @@ -323,7 +323,6 @@ def read_memory(ctx: click.Context, address: int, byte_count: int, ) - @main.command() @click.argument('sb_file', metavar='FILE', type=click.File('rb'), required=True) @click.pass_context @@ -407,7 +406,6 @@ def generate_key_blob(ctx: click.Context, dek_file: click.File, blob_file: click blob_file.write(write_response) # type: ignore - @main.group() @click.pass_context def key_provisioning(ctx: click.Context) -> None: @@ -424,43 +422,64 @@ def enroll(ctx: click.Context) -> None: @key_provisioning.command(name='set_user_key') -@click.argument('key_type', metavar='TYPE', type=INT(), required=True) +@click.argument('key_type', metavar='TYPE', type=str, required=True) @click.argument('file_and_size', metavar='FILE[,SIZE]', type=str, required=True) @click.pass_context -def set_user_key(ctx: click.Context, key_type: int, file_and_size: str) -> None: +def set_user_key(ctx: click.Context, key_type: str, file_and_size: str) -> None: """Send the user key specified by to bootloader. \b TYPE - Type of user key FILE - Binary file containing user key plaintext SIZE - If not specified, the entire will be sent. Otherwise, only send - the first bytes. The valid options of and - corresponding are documented in the target's Reference - Manual or User Manual. + the first bytes. + Available KEY TYPES: + 2 or 'OTFADKEK' OTFAD key + 3 or 'SBKEK' SB file encryption key + 7 or 'PRINCE0' Prince region 0 encryption key + 8 or 'PRINCE1' Prince region 1 encryption key + 9 or 'PRINCE2' Prince region 2 encryption key + 11 or 'USERKEK' User/Boot-image encryption key + 12 or 'UDS' Universal Device Secret for DICE + Note: The valid options of and corresponding are documented + in the target's Reference Manual or User Manual. + Note: Names are case insensitive """ file_path, size = parse_file_and_size(file_and_size) - + key_type_int = parse_key_prov_key_type(key_type) with open(file_path, 'rb') as key_file: key_data = key_file.read(size) with McuBoot(ctx.obj['interface']) as mboot: - response = mboot.kp_set_user_key(key_type=key_type, key_data=key_data) # type: ignore + response = mboot.kp_set_user_key(key_type=key_type_int, key_data=key_data) # type: ignore display_output([], mboot.status_code, ctx.obj['use_json']) @key_provisioning.command(name='set_key') -@click.argument('key_type', metavar='TYPE', type=int, required=True) +@click.argument('key_type', metavar='TYPE', type=str, required=True) @click.argument('key_size', metavar='SIZE', type=int, required=True) @click.pass_context -def set_key(ctx: click.Context, key_type: int, key_size: int) -> None: +def set_key(ctx: click.Context, key_type: str, key_size: int) -> None: """Generate bytes of the key specified by . \b - TYPE - type of key to generate + TYPE - type of key to generate, SIZE - size of key to generate + Available KEY TYPES: + 2 or 'OTFADKEK' OTFAD key + 3 or 'SBKEK' SB file encryption key + 7 or 'PRINCE0' Prince region 0 encryption key + 8 or 'PRINCE1' Prince region 1 encryption key + 9 or 'PRINCE2' Prince region 2 encryption key + 11 or 'USERKEK' User/Boot-image encryption key + 12 or 'UDS' Universal Device Secret for DICE + Note: The valid options of and corresponding are documented + in the target's Reference Manual or User Manual. + Note: Names are case insensitive """ + key_type_int = parse_key_prov_key_type(key_type) with McuBoot(ctx.obj['interface']) as mboot: - response = mboot.kp_set_intrinsic_key(key_type, key_size) + response = mboot.kp_set_intrinsic_key(key_type_int, key_size) display_output([], mboot.status_code, ctx.obj['use_json']) diff --git a/spsdk/apps/blhost_helper.py b/spsdk/apps/blhost_helper.py index e4f22b01..b25d9434 100644 --- a/spsdk/apps/blhost_helper.py +++ b/spsdk/apps/blhost_helper.py @@ -7,6 +7,9 @@ """Helper module for blhost application.""" +from spsdk.mboot.commands import KeyProvUserKeyType + + PROPERTIES_NAMES = { 'list-properties': 0, 'current-version': 1, @@ -40,6 +43,7 @@ 'ffr-keystore_update-opt': 29, } + def parse_property_tag(property_tag: str) -> int: """Convert the property as name or stringified number into integer. @@ -49,9 +53,19 @@ def parse_property_tag(property_tag: str) -> int: try: return int(property_tag, 0) except: - pass + return PROPERTIES_NAMES.get(property_tag, 0xFF) + + +def parse_key_prov_key_type(key_type: str) -> int: + """Convert the key type as name or stringified number into integer. + + :param key_type: Name or number of the Key type + :return: key type number + """ try: - return PROPERTIES_NAMES[property_tag] + return int(key_type, 0) except: - pass - return 0xFF + key_type = key_type.upper() + key_type_int = KeyProvUserKeyType.get(key_type, 0xFF) + assert isinstance(key_type_int, int) + return key_type_int diff --git a/spsdk/apps/nxpdebugmbox.py b/spsdk/apps/nxpdebugmbox.py index 96767217..08bdb9a1 100644 --- a/spsdk/apps/nxpdebugmbox.py +++ b/spsdk/apps/nxpdebugmbox.py @@ -24,6 +24,7 @@ logger = logging.getLogger("DebugMBox") +# pylint: disable=protected-access LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] PROTOCOL_VERSIONS = ['1.0', '1.1', '2.0', '2.1', '2.2'] diff --git a/spsdk/apps/pfr.py b/spsdk/apps/pfr.py index 2d4e6725..68329d10 100644 --- a/spsdk/apps/pfr.py +++ b/spsdk/apps/pfr.py @@ -9,22 +9,26 @@ import json import sys +import io # no_type_check decorator is used to suppress mypy's confusion in Click and cryptography libraries -from typing import (Iterable, List, Mapping, Optional, Tuple, Type, Union, - no_type_check) +from typing import (List, Optional, Tuple, Type, Union, no_type_check) import click -from jinja2 import Environment, FileSystemLoader +from click_option_group import MutuallyExclusiveOptionGroup, optgroup +from ruamel.yaml import YAML from spsdk import __version__ as spsdk_version -from spsdk import crypto, pfr +from spsdk import pfr, SPSDK_YML_INDENT +from spsdk.crypto import ( + RSAPublicKey, + load_certificate, + load_private_key, + load_public_key +) from spsdk.apps.utils import catch_spsdk_error -from spsdk.image.misc import dict_diff from spsdk.apps.elftosb_helper import RootOfTrustInfo -HTMLDataElement = Mapping[str, Union[str, Iterable[dict]]] -HTMLData = List[HTMLDataElement] PFRArea = Union[Type[pfr.CMPA], Type[pfr.CFPA]] @@ -44,29 +48,21 @@ def _get_pfr_class(area_name: str) -> PFRArea: @no_type_check -def _load_user_config(user_config_file: click.File) -> Optional[dict]: - """Load user JSON configuration file.""" - if user_config_file is None: - return None - return json.load(user_config_file) - - -@no_type_check -def _extract_public_key(file_path: str, password: Optional[str]) -> crypto.RSAPublicKey: - cert_candidate = crypto.load_certificate(file_path) +def _extract_public_key(file_path: str, password: Optional[str]) -> RSAPublicKey: + cert_candidate = load_certificate(file_path) if cert_candidate: return cert_candidate.public_key() - private_candidate = crypto.load_private_key(file_path, password.encode() if password else None) + private_candidate = load_private_key(file_path, password.encode() if password else None) if private_candidate: return private_candidate.public_key() - public_candidate = crypto.load_public_key(file_path) + public_candidate = load_public_key(file_path) if public_candidate: return public_candidate assert False, f"Unable to load secret file '{file_path}'." @no_type_check -def _extract_public_keys(secret_files: Tuple[str], password: Optional[str]) -> List[crypto.RSAPublicKey]: +def _extract_public_keys(secret_files: Tuple[str], password: Optional[str]) -> List[RSAPublicKey]: """Extract RSAPublic key from a file that contains Certificate, Private Key o Public Key.""" return [ _extract_public_key(file_path=source, password=password) @@ -74,42 +70,6 @@ def _extract_public_keys(secret_files: Tuple[str], password: Optional[str]) -> L ] -@no_type_check -def _get_data_for_html(area: PFRArea) -> HTMLData: - """Gather XML data and transform them into format used by template.""" - data: HTMLData = [] - for reg in area._get_registers(): - element: HTMLDataElement = { - 'name': reg.attrib['name'], - 'desc': reg.attrib['description'], - 'width': reg.attrib['width'], - 'offset': reg.attrib['offset'], - 'bitfields': [] - } - for reg_bitfield in area._get_bitfields(reg.attrib['name']): - if reg_bitfield.attrib['name'] == 'FIELD': - continue - bitfield = { - 'name': reg_bitfield.attrib['name'], - 'desc': reg_bitfield.attrib['description'], - 'width': reg_bitfield.attrib['width'], - 'offset': reg_bitfield.attrib['offset'], - 'bit_values': {} - } - for bf_value in reg_bitfield.findall('bit_field_value'): - bitfield['bit_values'][bf_value.attrib['value']] = bf_value.attrib['description'] - element['bitfields'].append(bitfield) - data.append(element) - return data - - -def _generate_html(area_name: str, data: List[dict]) -> str: - """Generate HTML content.""" - jinja_env = Environment(loader=FileSystemLoader(pfr.PFR_DATA_FOLDER)) - template = jinja_env.get_template("pfr_desc_template.html") - return template.render(area_name=area_name, data=data) - - @click.group() @click.version_option(spsdk_version, '-v', '--version') def main() -> int: @@ -117,12 +77,6 @@ def main() -> int: return 0 -@main.command() -def devices() -> None: - """List supported devices.""" - click.echo('\n'.join(pfr.CMPA.devices())) - - @main.command() @click.option('-d', '--device', type=click.Choice(pfr.CMPA.devices()), help="Device to use", required=True) @click.option('-r', '--revision', help="Chip revision; if not specified, most recent one will be used") @@ -131,18 +85,16 @@ def devices() -> None: @click.option('-o', '--output', type=click.Path(), required=False, help="Save the output into a file instead of console") @click.option('-f', '--full', is_flag=True, help="Show full config, including computed values") -def user_config(device: str, revision: str, area: str, output: click.Path, full: bool) -> None: - """Generate user configuration.""" +def get_cfg_template(device: str, revision: str, area: str, output: click.Path, full: bool) -> None: + """Generate user configuration template file.""" pfr_obj = _get_pfr_class(area)(device=device, revision=revision) - data = pfr_obj.generate_config(not full) - config = { - 'device': pfr_obj.device, - 'revision': pfr_obj.revision, - 'type': area, - 'settings': data - } - json_data = json.dumps(config, indent=2) - _store_output(json_data, output) + yaml = YAML(pure=True) + yaml.indent(sequence=SPSDK_YML_INDENT*2, offset=SPSDK_YML_INDENT) + data = pfr_obj.get_yaml_config(not full) + stream = io.StringIO() + yaml.dump(data, stream) + yaml_data = stream.getvalue() + _store_output(yaml_data, output) @main.command() @@ -156,58 +108,54 @@ def user_config(device: str, revision: str, area: str, output: click.Path, full: @click.option('-f', '--show-diff', is_flag=True, help="Show differences comparing to defaults") @click.option('-c', '--show-calc', is_flag=True, help="Show also calculated fields when displaying difference to " "defaults (--show-diff)") -def parse(device: str, revision: str, area: str, output: click.Path, binary: click.File, - show_calc: bool, show_diff: bool) -> None: +def parse_binary(device: str, revision: str, area: str, output: click.Path, binary: click.File, + show_calc: bool, show_diff: bool) -> None: """Parse binary a extract configuration.""" pfr_obj = _get_pfr_class(area)(device=device, revision=revision) data = binary.read() # type: ignore - parsed = pfr_obj.parse(data, exclude_computed=False) - if show_diff: - parsed = dict_diff( - pfr_obj.generate_config(exclude_computed=not show_calc), - parsed) - config = { - 'device': pfr_obj.device, - 'revision': pfr_obj.revision, - 'type': area, - 'settings': parsed - } - json_data = json.dumps(config, indent=2) - _store_output(json_data, output) + pfr_obj.parse(data, exclude_computed=False) + parsed = pfr_obj.get_yaml_config(exclude_computed=not show_calc, diff=show_diff) + yaml = YAML() + yaml.indent(sequence=4, offset=2) + stream = io.StringIO() + yaml.dump(parsed, stream) + yaml_data = stream.getvalue() + _store_output(yaml_data, output) @main.command() -@click.option('-c', '--user-config', 'user_config_file', type=click.File('r'), required=True, - help="JSON file with user configuration") +@optgroup.group('Root Of Trust Configuration', cls=MutuallyExclusiveOptionGroup) +@optgroup.option('-e', '--elf2sb-config', type=click.File('r'), + help='Specify Root Of Trust from configuration file used by elf2sb tool') +@optgroup.option('-f', '--secret-file', type=click.Path(exists=True), multiple=True, + help="Secret file (certificate, public key, private key); can be defined multiple times") +@click.option('-c', '--user-config', 'user_config_file', type=click.Path(), required=True, + help="YAML/JSON file with user configuration") @click.option('-o', '--output', type=click.Path(), required=True, help="Save the output into a file instead of console") @click.option('-a', '--add-seal', is_flag=True, help="Add seal mark digest at the end.") @click.option('-i', '--calc-inverse', is_flag=True, help="Calculate the INVERSE values CAUTION!!! It locks the settings") -@click.option('-e', '--elf2sb-config', type=click.File('r'), required=False, - help='Specify Root Of Trust from configuration file used by elf2sb tool') -@click.option('-f', '--secret-file', type=click.Path(exists=True), multiple=True, required=False, - help="Secret file (certificate, public key, private key); can be defined multiple times") @click.option('-p', '--password', help="Password when using Encrypted private keys as --secret-file") -def generate(output: click.Path, user_config_file: click.File, add_seal: bool, calc_inverse: bool, - elf2sb_config: click.File, secret_file: Tuple[str], password: str) -> None: +def generate_binary(output: click.Path, user_config_file: click.Path, add_seal: bool, calc_inverse: bool, + elf2sb_config: click.File, secret_file: Tuple[str], password: str) -> None: """Generate binary data.""" - user_config = _load_user_config(user_config_file) + pfr_config = pfr.PfrConfiguration(str(user_config_file)) root_of_trust = None + keys = None if elf2sb_config: - keys = RootOfTrustInfo(json.load(elf2sb_config)).public_keys # type: ignore - root_of_trust = tuple(keys) + public_keys = RootOfTrustInfo(json.load(elf2sb_config)).public_keys # type: ignore + root_of_trust = tuple(public_keys) if secret_file: root_of_trust = secret_file - area = user_config['type'] - pfr_obj = _get_pfr_class(area)(device=user_config['device'], revision=user_config.get('revision')) - if area == 'cmpa' and not root_of_trust: - click.echo('Error: CMPA page requires either --secret-file(s) or --elf2sb-config') - sys.exit(1) - pfr_obj.keys = _extract_public_keys(root_of_trust, password) if area == 'cmpa' else None - pfr_obj.user_config = user_config['settings'] - data = pfr_obj.export(add_seal=add_seal, compute_inverses=calc_inverse) + area = pfr_config.type + if area.lower() == 'cmpa' and root_of_trust: + keys = _extract_public_keys(root_of_trust, password) + pfr_obj = _get_pfr_class(area)(device=pfr_config.device, revision=pfr_config.revision) + pfr_obj.set_config(pfr_config, raw=not calc_inverse) + + data = pfr_obj.export(add_seal=add_seal, keys=keys) _store_output(data, output, 'wb') @@ -222,13 +170,96 @@ def generate(output: click.Path, user_config_file: click.File, add_seal: bool, c def info(device: str, revision: str, area: str, output: click.Path, open_result: bool) -> None: """Generate HTML page with brief description of CMPA/CFPA configuration fields.""" pfr_obj = _get_pfr_class(area)(device=device, revision=revision) - data = _get_data_for_html(pfr_obj) - html_output = _generate_html(pfr_obj.__class__.__name__, data) + html_output = pfr_obj.registers.generate_html( + f"{device.upper()} - {area.upper()}", + pfr_obj.DESCRIPTION, + regs_exclude=["SHA256_DIGEST"], + fields_exclude=["FIELD"] + ) _store_output(html_output, output) if open_result: # pragma: no cover # can't test opening the html document click.launch(f'{output}') +@main.command() +def devices() -> None: + """List supported devices.""" + click.echo('\n'.join(pfr.CMPA.devices())) + + +######################################################################################### +# +# Depreciated commands part +# +######################################################################################### + + +def echo_deprecated() -> None: + """Print message about deprecated functions.""" + click.secho(f"You are using deprecated function of SPSDK PFR tool.\n"+ \ + f"There is list of deprecated function successors:\n" + \ + f" - user-config -> get-cfg-template\n" + \ + f" - parse -> parse-binary\n" + \ + f" - generate -> generate-binary\n", + fg="yellow") + + +@main.command() +@click.option('-d', '--device', type=click.Choice(pfr.CMPA.devices()), help="Device to use", required=True) +@click.option('-r', '--revision', help="Chip revision; if not specified, most recent one will be used") +@click.option('-t', '--type', 'area', required=True, type=click.Choice(['cmpa', 'cfpa']), + help='Select PFR partition') +@click.option('-o', '--output', type=click.Path(), required=False, + help="Save the output into a file instead of console") +@click.option('-f', '--full', is_flag=True, help="Show full config, including computed values") +@click.pass_context +def user_config(ctx: click.Context, device: str, revision: str, area: str, output: click.Path, full: bool) -> None: + """This is depreciated command for get-cfg-template.""" + echo_deprecated() + ctx.invoke(get_cfg_template, device=device, revision=revision, area=area, output=output, full=full) + + +@main.command() +@click.option('-c', '--user-config', 'user_config_file', type=click.Path(), required=True, + help="YAML/JSON file with user configuration") +@click.option('-o', '--output', type=click.Path(), required=True, + help="Save the output into a file instead of console") +@click.option('-a', '--add-seal', is_flag=True, + help="Add seal mark digest at the end.") +@click.option('-i', '--calc-inverse', is_flag=True, + help="Calculate the INVERSE values CAUTION!!! It locks the settings") +@click.option('-e', '--elf2sb-config', type=click.File('r'), required=False, + help='Specify Root Of Trust from configuration file used by elf2sb tool') +@click.option('-f', '--secret-file', type=click.Path(exists=True), multiple=True, required=False, + help="Secret file (certificate, public key, private key); can be defined multiple times") +@click.option('-p', '--password', help="Password when using Encrypted private keys as --secret-file") +@click.pass_context +def generate(ctx: click.Context, output: click.Path, user_config_file: click.Path, add_seal: bool, calc_inverse: bool, + elf2sb_config: click.File, secret_file: Tuple[str], password: str) -> None: + """This is depreciated command for generate-binary.""" + echo_deprecated() + ctx.invoke(generate_binary, output=output, user_config_file=user_config_file, add_seal=add_seal, + calc_inverse=calc_inverse, elf2sb_config=elf2sb_config, secret_file=secret_file, password=password) + +@main.command() +@click.option('-d', '--device', type=click.Choice(pfr.CMPA.devices()), help="Device to use", required=True) +@click.option('-r', '--revision', help="Chip revision; if not specified, most recent one will be used") +@click.option('-t', '--type', 'area', required=True, type=click.Choice(['cmpa', 'cfpa']), + help='Select PFR partition') +@click.option('-o', '--output', type=click.Path(), required=False, + help="Save the output into a file instead of console") +@click.option('-b', '--binary', type=click.File('rb'), required=True, help="Binary to parse") +@click.option('-f', '--show-diff', is_flag=True, help="Show differences comparing to defaults") +@click.option('-c', '--show-calc', is_flag=True, help="Show also calculated fields when displaying difference to " + "defaults (--show-diff)") +@click.pass_context +def parse(ctx: click.Context, device: str, revision: str, area: str, output: click.Path, binary: click.File, + show_calc: bool, show_diff: bool) -> None: + """This is depreciated command for parse-binary.""" + echo_deprecated() + ctx.invoke(parse_binary, device=device, revision=revision, area=area, + output=output, binary=binary, show_calc=show_calc, show_diff=show_diff) + @catch_spsdk_error def safe_main() -> None: """Call the main function.""" diff --git a/spsdk/apps/pfrc.py b/spsdk/apps/pfrc.py index 28801da5..af688b91 100644 --- a/spsdk/apps/pfrc.py +++ b/spsdk/apps/pfrc.py @@ -13,6 +13,7 @@ import click +from spsdk.pfr.pfr import PfrConfiguration from spsdk.apps.utils import catch_spsdk_error from spsdk.pfr import Translator, Processor, PFR_DATA_FOLDER @@ -21,40 +22,40 @@ @click.command() -@click.option('-m', '--cmpa-config', required=True, type=click.File('r'), +@click.option('-m', '--cmpa-config', required=True, type=click.Path(), help='Path to CMPA config json file') -@click.option('-f', '--cfpa-config', required=True, type=click.File('r'), +@click.option('-f', '--cfpa-config', required=True, type=click.Path(), help='Path to CFPA config json file') @click.option('-r', '--rules-file', required=False, type=click.File('r'), default=RULES_FILE, help='Custom file containing checker rules') @click.option('-d', '--debug', is_flag=True, default=False, help='Enable debugging output') -def main(cmpa_config: click.File, cfpa_config: click.File, rules_file: click.File, debug: bool) -> None: +def main(cmpa_config: click.Path, cfpa_config: click.Path, rules_file: click.File, debug: bool) -> None: """Utility to search for brick-conditions in PFR settings.""" logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) - cmpa_data = json.load(cmpa_config) # type: ignore - cfpa_data = json.load(cfpa_config) # type: ignore + cmpa_prf_cfg = PfrConfiguration(str(cmpa_config)) + cfpa_prf_cfg = PfrConfiguration(str(cfpa_config)) rules = json.load(rules_file) # type: ignore - translator = Translator(cmpa_data=cmpa_data, cfpa_data=cfpa_data) + translator = Translator(cmpa=cmpa_prf_cfg, cfpa=cfpa_prf_cfg) processor = Processor(translator=translator) for rule in rules: try: - print("Requirement: {}".format(rule["req_id"])) - print(f"{rule['desc']}...") - result, cond = processor.process(rule['cond']) - print(f"Brick condition: {rule['cond']}") - print(cond) - print(f"FAIL: you are going to brick your device\n{rule['msg']}" if result \ + click.echo("Requirement: {}".format(rule["req_id"])) + click.echo(f"{rule['desc']}...") + result, condition = processor.process(rule['cond']) + click.echo(f"Brick condition: {rule['cond']}") + click.echo(condition) + click.echo(f"FAIL: you are going to brick your device\n{rule['msg']}" if result \ else "OK: Brick condition not fulfilled") except SyntaxError as e: - print(f"\nERROR: Unable to parse: '{e}'") + click.echo(f"\nERROR: Unable to parse: '{e}'") except (KeyError, ValueError, TypeError) as e: - print(f"\nERROR: Unable to lookup identifier: {e}") - except Exception as e: - print(f"Error e({e}) while evaluating {rule['cond']}") - print("-" * 40) + click.echo(f"\nERROR: Unable to lookup identifier: {e}") + except Exception as e: # pylint: disable=broad-except + click.echo(f"Error e({e}) while evaluating {rule['cond']}") + click.echo("-" * 40) @catch_spsdk_error diff --git a/spsdk/apps/shadowregs.py b/spsdk/apps/shadowregs.py index cb1da307..18dfbc89 100644 --- a/spsdk/apps/shadowregs.py +++ b/spsdk/apps/shadowregs.py @@ -7,7 +7,6 @@ """Main Debug Authentication Tool application.""" import logging - import sys import os @@ -16,26 +15,27 @@ import click from spsdk import __version__ as spsdk_version -from spsdk.dat import ShadowRegisters -from spsdk.dat.shadow_regs import enable_debug +from spsdk.shadowregs import ShadowRegisters, enable_debug from spsdk.debuggers.utils import DebugProbeUtils from spsdk.exceptions import SPSDKError -from spsdk.utils.registers import RegConfig, RegsRegister +from spsdk.utils.registers import Registers, RegsRegister +from spsdk.utils.reg_config import RegConfig from spsdk.apps.utils import catch_spsdk_error from spsdk import SPSDK_DATA_FOLDER logger = logging.getLogger("ShadowRegs") +# pylint: disable=protected-access LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] -CONFIG_DIR = os.path.join(SPSDK_DATA_FOLDER, "shadow_regs") +CONFIG_DIR = os.path.join(SPSDK_DATA_FOLDER, "shadowregs") CONFIG_FILE = "database.json" -def _open_registers(pass_obj: Dict) -> ShadowRegisters: - """Method opens Registers object based on input arguments. +def _open_shadow_registers(pass_obj: Dict) -> ShadowRegisters: + """Method opens ShadowRegisters object based on input arguments. :param pass_obj: Input dictionary with arguments. - :return: Active Registers object. + :return: Active ShadowRegisters object. :raise SPSDKError: Raised with any kind of problems with debug probe. """ config_file = pass_obj['config_file'] @@ -43,6 +43,7 @@ def _open_registers(pass_obj: Dict) -> ShadowRegisters: serial_no = pass_obj['serial_no'] debug_probe_params = pass_obj['debug_probe_params'] device = pass_obj['device'] + revision = pass_obj['revision'] if device not in RegConfig.devices(config_file): raise SPSDKError("Invalid or none device parameter(-dev). Use 'listdevs' command to get supported devices.") @@ -66,7 +67,8 @@ def _open_registers(pass_obj: Dict) -> ShadowRegisters: return ShadowRegisters( debug_probe=debug_probe, config=regs_cfg, - device=device + device=device, + revision=revision ) @click.group() @@ -80,13 +82,15 @@ def _open_registers(pass_obj: Dict) -> ShadowRegisters: @click.option('-s', '--serial-no', help="Serial number of debug probe to avoid select menu after startup.") @click.option('-dev', '--device', type=str, help="The connected device - to list supported devices use 'listdevs' command.") +@click.option('-r', '--revision', help="Chip revision; if not specified, most recent one will be used") @click.option('-o', '--debug-probe-option', multiple=True, help="This option could be used " "multiply to setup non-standard option for debug probe.") @click.version_option(spsdk_version, '-v', '--version') @click.help_option('--help') @click.pass_context def main(ctx: click.Context, interface: str, log_level: str, - serial_no: str, debug_probe_option: List[str], device: str) -> int: + serial_no: str, debug_probe_option: List[str], device: str, + revision: str) -> int: """NXP Shadow Registers control Tool.""" logging.basicConfig(level=log_level.upper()) logger.setLevel(level=log_level.upper()) @@ -106,7 +110,8 @@ def main(ctx: click.Context, interface: str, log_level: str, 'interface': interface, 'serial_no': serial_no, 'debug_probe_params': probe_user_params, - 'device': device + 'device': device, + 'revision': revision or 'latest' } return 0 @@ -123,7 +128,7 @@ def main(ctx: click.Context, interface: str, log_level: str, def saveconfig(pass_obj: dict, filename: str = "sr_config.yml", raw: bool = False) -> None: """Save current state of shadow registers to YML file.""" try: - shadow_regs: ShadowRegisters = _open_registers(pass_obj) + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) shadow_regs.reload_registers() shadow_regs.create_yml_config(filename, raw) click.echo(f"The Shadow registers has been saved into {filename} YAML file") @@ -141,7 +146,7 @@ def saveconfig(pass_obj: dict, filename: str = "sr_config.yml", raw: bool = Fals def loadconfig(pass_obj: dict, filename: str = "sr_config.yml", raw: bool = False) -> None: """Load new state of shadow registers from YML file into microcontroller.""" try: - shadow_regs: ShadowRegisters = _open_registers(pass_obj) + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) shadow_regs.load_yml_config(filename, raw) shadow_regs.sets_all_registers() click.echo(f"The Shadow registers has been loaded by configuration in {filename} YAML file") @@ -157,10 +162,10 @@ def printregs(pass_obj: dict, rich: bool = False) -> None: In case of needed more information, there is also provided rich format of print. """ try: - shadow_regs: ShadowRegisters = _open_registers(pass_obj) + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) shadow_regs.reload_registers() - for reg in shadow_regs.regs.registers: + for reg in shadow_regs.regs.get_registers(): click.echo(f"Register Name: {reg.name}") click.echo(f"Register value: {reg.get_hex_value()}") if rich: @@ -177,7 +182,7 @@ def printregs(pass_obj: dict, rich: bool = False) -> None: @click.pass_obj def getreg(pass_obj: dict, reg: str) -> None: """The command prints the current value of one shadow register.""" - shadow_regs: ShadowRegisters = _open_registers(pass_obj) + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) try: register: RegsRegister = shadow_regs.regs.find_reg(reg) shadow_regs.reload_register(register) @@ -191,7 +196,7 @@ def getreg(pass_obj: dict, reg: str) -> None: @click.pass_obj def setreg(pass_obj: dict, reg: str, reg_val: str) -> None: """The command sets a value of one shadow register defined by parameter.""" - shadow_regs: ShadowRegisters = _open_registers(pass_obj) + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) try: shadow_regs.set_register(reg, reg_val) click.echo(f"The Shadow register {reg} has been set to {reg_val} value") @@ -202,8 +207,8 @@ def setreg(pass_obj: dict, reg: str, reg_val: str) -> None: @click.pass_obj def reset(pass_obj: dict) -> None: """The command resets connected device.""" - shadow_regs: ShadowRegisters = _open_registers(pass_obj) - shadow_regs._probe.reset() + shadow_regs: ShadowRegisters = _open_shadow_registers(pass_obj) + shadow_regs.probe.reset() click.echo(f"The target has been reset.") @@ -212,8 +217,31 @@ def reset(pass_obj: dict) -> None: def listdevs(pass_obj: dict) -> None: """The command prints a list of supported devices.""" config_filename = pass_obj['config_file'] - for ix, device in enumerate(RegConfig.devices(config_filename)): - click.echo(f"{ix:03}: {device}") + for index, device in enumerate(RegConfig.devices(config_filename)): + click.echo(f"{index:03}: {device}") + +@main.command() +@click.option('-o', '--output', type=click.Path(), required=True, + help="Save the output into a file instead of console") +@click.option('-p', '--open', 'open_result', is_flag=True, help="Open the generated description file") +@click.pass_obj +def info(pass_obj: dict, output: str, open_result: bool) -> None: + """The command generate HTML of Shadow registers.""" + config = RegConfig(pass_obj['config_file']) + device = pass_obj['device'] + revision = pass_obj['revision'] + registers = Registers(device) + rev = revision if revision != "latest" else config.get_latest_revision(device) + registers.load_registers_from_xml(config.get_data_file(device, rev)) + html_output = registers.generate_html( + f"{device} - Shadow Registers", + f"The table with Shadow registers description for {device}" + ) + with open(output, "w", encoding="utf-8") as f: + f.write(html_output) + + if open_result: # pragma: no cover # can't test opening the html document + click.launch(f'{output}') @catch_spsdk_error def safe_main() -> None: diff --git a/spsdk/apps/spsdk_apps.py b/spsdk/apps/spsdk_apps.py index 0e0dea39..dfb9705f 100644 --- a/spsdk/apps/spsdk_apps.py +++ b/spsdk/apps/spsdk_apps.py @@ -41,13 +41,13 @@ def main() -> int: main.add_command(elftosb_main, name='elftosb') main.add_command(nxpcertgen_main, name='nxpcertgen') main.add_command(nxpdebugmbox_main, name='nxpdebugmbox') -main.add_command(nxpdevscan_main, name='nxpdscan') +main.add_command(nxpdevscan_main, name='nxpdevscan') main.add_command(nxpkeygen_main, name='nxpkeygen') main.add_command(pfr_main, name='pfr') main.add_command(pfrc_main, name='pfrc') main.add_command(sdphost_main, name='sdphost') main.add_command(sdpshost_main, name='sdpshost') -main.add_command(shadowregs_main, name='shadowreg') +main.add_command(shadowregs_main, name='shadowregs') @catch_spsdk_error def safe_main() -> Any: diff --git a/spsdk/apps/utils.py b/spsdk/apps/utils.py index d8f3ad0e..903b6502 100644 --- a/spsdk/apps/utils.py +++ b/spsdk/apps/utils.py @@ -85,9 +85,10 @@ def get_interface(module: str, port: str = None, usb: str = None, }[module] devices = [] if port: - # it seems that the variable baudrate doesn't work properly - name = port.split(',')[0] if ',' in port else port - devices = interface_module.scan_uart(port=name, timeout=timeout) # type: ignore + port_parts = port.split(',') + name = port_parts.pop(0) + baudrate = int(port_parts.pop(), 0) if port_parts else None + devices = interface_module.scan_uart(port=name, baudrate=baudrate, timeout=timeout) # type: ignore if len(devices) != 1: raise SPSDKError(f"Cannot ping device on UART port '{name}'.") if usb: diff --git a/spsdk/dat/__init__.py b/spsdk/dat/__init__.py index 436f53f8..f3772f34 100644 --- a/spsdk/dat/__init__.py +++ b/spsdk/dat/__init__.py @@ -10,4 +10,3 @@ from .debug_credential import DebugCredential from .dac_packet import DebugAuthenticationChallenge from .dar_packet import DebugAuthenticateResponse -from .shadow_regs import ShadowRegisters diff --git a/spsdk/dat/debug_mailbox.py b/spsdk/dat/debug_mailbox.py index 1f1e73a8..2be51e7b 100644 --- a/spsdk/dat/debug_mailbox.py +++ b/spsdk/dat/debug_mailbox.py @@ -4,7 +4,7 @@ # Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -"""Module for NXP SPDK DebugMailbox support.""" +"""Module for NXP SPSDK DebugMailbox support.""" import logging from time import sleep @@ -31,7 +31,7 @@ def __init__(self, debug_probe: DebugProbe, reset: bool = True, moredelay: float # setup registers and register bitfields self.registers = REGISTERS - # Proceed with initiation (Resynchronisation request) + # Proceed with initiation (Resynchronization request) # The communication to the DM is initiated by the debugger. # It does so by writing the RESYNCH_REQ bit of the CSW (Control and Status Word) diff --git a/spsdk/dat/dm_commands.py b/spsdk/dat/dm_commands.py index ab073c73..61c10706 100644 --- a/spsdk/dat/dm_commands.py +++ b/spsdk/dat/dm_commands.py @@ -9,7 +9,7 @@ import time from typing import Any, List -from spsdk.image.misc import format_value +from spsdk.utils.misc import format_value from .debug_mailbox import DebugMailbox, logger diff --git a/spsdk/data/pfr/cfpa/database.json b/spsdk/data/pfr/cfpa/database.json index 356d2dde..3c86548e 100644 --- a/spsdk/data/pfr/cfpa/database.json +++ b/spsdk/data/pfr/cfpa/database.json @@ -23,15 +23,15 @@ "latest": "a1", "address": "0x3_DE00" }, - "lpc550x": - { + "lpc550x": + { "revisions": { "a1": "niobe4nano_a1.xml" }, "latest": "a1", "address": "0x3_DE00" }, - "lpc55s0x": + "lpc55s0x": { "revisions": { "a1": "niobe4nano_a1.xml" @@ -39,52 +39,67 @@ "latest": "a1", "address": "0x3_DE00" }, - "lpc552x": + "lpc552x": { "revisions": { "a1": "niobe4_a1.xml" }, "latest": "a1", "address": "0x9_DE00" - }, - "lpc55s2x": - { + }, + "lpc55s2x": + { "revisions": { "a1": "niobe4_a1.xml" - }, + }, "latest": "a1", "address": "0x9_DE00" - }, - "lpc55s3x": + }, + "lpc55s3x": { "revisions": { "a1": "niobe4analog_a1.xml" - }, + }, "latest": "a1", "address": "0x3_DC00", "seal_start":"CFPA_CRC32", - "seal_count": 1 + "seal_count": 1, + "computed_fields": { + "VENDOR_USAGE":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_NS_PIN":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_NS_DFLT":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"} + } }, "lpc553x": { "revisions": { "a1": "niobe4analog_a1.xml" - }, + }, "latest": "a1", "address": "0x3_DC00", "seal_start": "CFPA_CRC32", - "seal_count": 1 + "seal_count": 1, + "computed_fields": { + "VENDOR_USAGE":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_NS_PIN":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_NS_DFLT":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"} + } } }, "computed_registers": [ "SHA256_DIGEST" ], - "computed_fields": [ - "INVERSE_VALUE" - ], + "computed_fields": { + "VENDOR_USAGE":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_PIN":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "DCFG_CC_SOCU_DFLT":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"} + }, "ignored_registers": [ "PRINCE_REGION" ], + "ignored_fields": [ + "FIELD" + ], "seal_start": "SHA256_DIGEST0", "seal_count": 8 } diff --git a/spsdk/data/pfr/cfpa/niobe4_a1.xml b/spsdk/data/pfr/cfpa/niobe4_a1.xml index 87a044b3..97456735 100644 --- a/spsdk/data/pfr/cfpa/niobe4_a1.xml +++ b/spsdk/data/pfr/cfpa/niobe4_a1.xml @@ -92,16 +92,12 @@ - - - - - - - - - + + + + + @@ -152,16 +148,12 @@ - - - - - - - - - + + + + + diff --git a/spsdk/data/pfr/cfpa/niobe4mini_a1.xml b/spsdk/data/pfr/cfpa/niobe4mini_a1.xml index 87a044b3..faa98a11 100644 --- a/spsdk/data/pfr/cfpa/niobe4mini_a1.xml +++ b/spsdk/data/pfr/cfpa/niobe4mini_a1.xml @@ -82,32 +82,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + @@ -142,32 +128,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + diff --git a/spsdk/data/pfr/cfpa/niobe4nano_a1.xml b/spsdk/data/pfr/cfpa/niobe4nano_a1.xml index 87a044b3..faa98a11 100644 --- a/spsdk/data/pfr/cfpa/niobe4nano_a1.xml +++ b/spsdk/data/pfr/cfpa/niobe4nano_a1.xml @@ -82,32 +82,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + @@ -142,32 +128,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + diff --git a/spsdk/data/pfr/cmpa/database.json b/spsdk/data/pfr/cmpa/database.json index 74d44be8..6ea4e46b 100644 --- a/spsdk/data/pfr/cmpa/database.json +++ b/spsdk/data/pfr/cmpa/database.json @@ -23,7 +23,7 @@ "latest": "a1", "address": "0x3_E400" }, - "lpc550x": + "lpc550x": { "revisions": { "a1": "niobe4nano_a1.xml" @@ -31,7 +31,7 @@ "latest": "a1", "address": "0x3_E400" }, - "lpc55s0x": + "lpc55s0x": { "revisions": { "a1": "niobe4nano_a1.xml" @@ -39,7 +39,7 @@ "latest": "a1", "address": "0x3_E400" }, - "lpc55s2x": + "lpc55s2x": { "revisions": { "a1": "niobe4_a1.xml" @@ -47,7 +47,7 @@ "latest": "a1", "address": "0x9_E400" }, - "lpc552x": { + "lpc552x": { "revisions": { "a1": "niobe4_a1.xml" }, @@ -76,12 +76,23 @@ } }, "computed_registers": [ - "ROTKH", "SHA256_DIGEST" + "SHA256_DIGEST" ], - "computed_fields": [ - "INVERSE_VALUE" + "grouped_registers": [ + { + "name": "ROTKH", + "width": 256, + "reverse": 1, + "description":"ROTKH0 for Root of Trust Keys Table hash[255:224] ROTKH1 for Root of Trust Keys Table hash[223:192] ROTKH2 for Root of Trust Keys Table hash[191:160] ROTKH3 for Root of Trust Keys Table hash[159:128] ROTKH4 for Root of Trust Keys Table hash[127:96] ROTKH5 for Root of Trust Keys Table hash[95:64] ROTKH6 for Root of Trust Keys Table hash[63:32] ROTKH7 for Root of Trust Keys Table hash[31:0]" + } + ], + "computed_fields": { + "CC_SOCU_PIN":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"}, + "CC_SOCU_DFLT":{"INVERSE_VALUE": "pfr_reg_inverse_high_half"} + }, + "ignored_fields": [ + "FIELD" ], "seal_start": "SHA256_DIGEST0", "seal_count": 8 - } diff --git a/spsdk/data/pfr/cmpa/niobe4_a1.xml b/spsdk/data/pfr/cmpa/niobe4_a1.xml index d59e675d..af03bb04 100644 --- a/spsdk/data/pfr/cmpa/niobe4_a1.xml +++ b/spsdk/data/pfr/cmpa/niobe4_a1.xml @@ -7,8 +7,9 @@ - - + + + @@ -81,16 +82,12 @@ - - - - - - - - - + + + + + @@ -141,16 +138,12 @@ - - - - - - - - - + + + + + diff --git a/spsdk/data/pfr/cmpa/niobe4analog_a1.xml b/spsdk/data/pfr/cmpa/niobe4analog_a1.xml index af865541..397238ce 100644 --- a/spsdk/data/pfr/cmpa/niobe4analog_a1.xml +++ b/spsdk/data/pfr/cmpa/niobe4analog_a1.xml @@ -94,7 +94,7 @@ - + @@ -131,7 +131,7 @@ - + diff --git a/spsdk/data/pfr/cmpa/niobe4mini_a1.xml b/spsdk/data/pfr/cmpa/niobe4mini_a1.xml index e0cde15a..bd71d726 100644 --- a/spsdk/data/pfr/cmpa/niobe4mini_a1.xml +++ b/spsdk/data/pfr/cmpa/niobe4mini_a1.xml @@ -7,8 +7,9 @@ - - + + + @@ -70,32 +71,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + @@ -130,32 +117,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + diff --git a/spsdk/data/pfr/cmpa/niobe4nano_a1.xml b/spsdk/data/pfr/cmpa/niobe4nano_a1.xml index 632200e1..a8541a19 100644 --- a/spsdk/data/pfr/cmpa/niobe4nano_a1.xml +++ b/spsdk/data/pfr/cmpa/niobe4nano_a1.xml @@ -70,32 +70,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + @@ -130,32 +116,18 @@ - - - - - + - - - - - - - - - - - - - - + + + + - + diff --git a/spsdk/data/pfr/rules.json b/spsdk/data/pfr/rules.json index 734428a3..d9cae31c 100644 --- a/spsdk/data/pfr/rules.json +++ b/spsdk/data/pfr/rules.json @@ -43,8 +43,8 @@ }, { "req_id": "2.1", - "desc": "This CMPA_PROG_IN_PROGRESS must be always 0x00000000. Only ROM bootlader is allowed to write anything to this field.", - "msg": "The CMPA_PROG_IN_PROGESS must be set to 0!", + "desc": "This CMPA_PROG_IN_PROGRESS must be always 0x00000000. Only ROM bootloader is allowed to write anything to this field.", + "msg": "The CMPA_PROG_IN_PROGRESS must be set to 0!", "cond": "CFPA.CMPA_PROG_IN_PROGRESS != 0" }, { diff --git a/spsdk/data/pfr/pfr_desc_template.html b/spsdk/data/regs/regs_desc_template.html similarity index 88% rename from spsdk/data/pfr/pfr_desc_template.html rename to spsdk/data/regs/regs_desc_template.html index 5467e773..aee4f916 100644 --- a/spsdk/data/pfr/pfr_desc_template.html +++ b/spsdk/data/regs/regs_desc_template.html @@ -15,13 +15,8 @@ } - {% if area_name == 'CMPA' %} -

CMPA

-

Customer Manufacturing Programable Area

- {% elif area_name == 'CFPA' %} -

CFPA

-

Customer In-Field Programable Area

- {% endif %} +

{{ heading1 }}

+

{{ heading2 }}

diff --git a/spsdk/data/shadow_regs/database.json b/spsdk/data/shadow_regs/database.json deleted file mode 100644 index c7e74c93..00000000 --- a/spsdk/data/shadow_regs/database.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "devices": { - "imxrt595": { - "revisions": { - "b0": "imxrt595_b0.xml" - }, - "latest": "b0", - "address": "0x4013_0000", - "inverted_regs": { - "DCFG_CC_SOCU": "DCFG_CC_SOCU_AP" - }, - "computed_fields": { - "DCFG_CC_SOCU": {"RSVD": "comalg_dcfg_cc_socu_rsvd", "CRC8": "comalg_dcfg_cc_socu_crc8"}, - "SEC_BOOT_CFG[5]": {"RESERVED": "comalg_do_nothig"} - } - }, - "imxrt685": { - "revisions": { - "b0": "imxrt685_b0.xml" - }, - "latest": "b0", - "address": "0x4013_0000", - "inverted_regs": { - "DCFG_CC_SOCU": "DCFG_CC_SOCU_AP" - }, - "computed_fields": { - "DCFG_CC_SOCU": {"CRC8": "comalg_dcfg_cc_socu_crc8"}, - "SEC_BOOT_CFG[5]": {"RESERVED": "comalg_do_nothig"} - } - } - } -} diff --git a/spsdk/data/shadow_regs/imxrt595_b0.xml b/spsdk/data/shadow_regs/imxrt595_b0.xml deleted file mode 100644 index a4f881e1..00000000 --- a/spsdk/data/shadow_regs/imxrt595_b0.xml +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spsdk/data/shadowregs/database.json b/spsdk/data/shadowregs/database.json new file mode 100644 index 00000000..773ed1c2 --- /dev/null +++ b/spsdk/data/shadowregs/database.json @@ -0,0 +1,50 @@ +{ + "devices": { + "imxrt595": { + "revisions": { + "b0": "imxrt595_b0.xml" + }, + "latest": "b0", + "address": "0x4013_0000", + "inverted_regs": { + "DCFG_CC_SOCU": "DCFG_CC_SOCU_AP" + }, + "computed_fields": { + "DCFG_CC_SOCU": {"RSVD": "comalg_dcfg_cc_socu_rsvd", "CRC8": "comalg_dcfg_cc_socu_crc8"}, + "SEC_BOOT_CFG[5]": {"RESERVED": "comalg_do_nothing"} + } + }, + "imxrt685": { + "revisions": { + "b0": "imxrt685_b0.xml" + }, + "latest": "b0", + "address": "0x4013_0000", + "inverted_regs": { + "DCFG_CC_SOCU": "DCFG_CC_SOCU_AP" + }, + "computed_fields": { + "DCFG_CC_SOCU": {"CRC8": "comalg_dcfg_cc_socu_crc8"}, + "SEC_BOOT_CFG[5]": {"RESERVED": "comalg_do_nothing"} + } + } + }, + "grouped_registers": [ + { + "name": "OTFAD_KEK_SEED", + "width": 128, + "description":"When OTP key store is used (USE_PUF = 0), this 128-bit user programmed value is used to derive OTFAD_KEK. OTFAD_KEK[127:0] = AES_ENCRYPT (OTP_MASTER_KEK, OTFAD_KEK_SEED[127:0]);" + }, + { + "name": "OTP_MASTER_KEY", + "width": 256, + "description":"Master Key used to derive different usage keys (HMAC_KEY, ENC_IMAGE_KEY, SB2_KEK, OTFAD_KEK). - ENC_BOOT_HMAC_KEY[127:0] = AES_ENCRYPT (OTP_MASTER_KEK, 0x00000000000000000000000000000000); - ENC_BOOT_AES_KEY[256:0] = AES_ENCRYPT (OTP_MASTER_KEK, 0x00000000000000000000000000000001) || AES_ENCRYPT (OTP_MASTER_KEK, 0x00000000000000000000000000000002); - SB_KEK[256:0] = AES_ENCRYPT (OTP_MASTER_KEK, 0x00000000000000000000000000000003) || AES_ENCRYPT (OTP_MASTER_KEK, 0x00000000000000000000000000000004); OR if SBKEK_SEED != 0 then SB_KEK[255:0] = AES_ENCRYPT (OTP_MASTER_KEK, SBKEK_SEED[255:0]); - OTFAD_KEK[127:0] = AES_ENCRYPT (OTP_MASTER_KEK, OTFAD_KEK_SEED[127:0]);" + }, + { + "name": "RKTH", + "width": 256, + "reverse": 1, + "description":"SHA256 hash digest of hash of four Root Of Trust Keys (modulus || exponent). For i in 0..3: Let M[i] = BE(Modulus i) Let E[i] = BE(Exponent i) Let RKH[i] = SHA256( M[i] || E[i] ) Let RKTH = SHA256( RKH[0] || RKH[1] || RKH[2] || RKH[3] ) Note: Documentation update needed when using blhost due to endianness issue." + } + ] +} diff --git a/spsdk/data/shadowregs/imxrt595_b0.xml b/spsdk/data/shadowregs/imxrt595_b0.xml new file mode 100644 index 00000000..c235d039 --- /dev/null +++ b/spsdk/data/shadowregs/imxrt595_b0.xml @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/shadow_regs/imxrt685_b0.xml b/spsdk/data/shadowregs/imxrt685_b0.xml similarity index 56% rename from spsdk/data/shadow_regs/imxrt685_b0.xml rename to spsdk/data/shadowregs/imxrt685_b0.xml index 0246c65c..c969db19 100644 --- a/spsdk/data/shadow_regs/imxrt685_b0.xml +++ b/spsdk/data/shadowregs/imxrt685_b0.xml @@ -1,116 +1,111 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + - + @@ -127,7 +122,7 @@ values to shadow registers corresponding to OTP words (95 & 104) with this b - + @@ -135,33 +130,24 @@ values to shadow registers corresponding to OTP words (95 & 104) with this b - + - - - + + + - - + + - - - + + + @@ -171,35 +157,33 @@ values to shadow registers corresponding to OTP words (95 & 104) with this b - + - - - + + + - - + + - + - - + + @@ -209,19 +193,19 @@ values to shadow registers corresponding to OTP words (95 & 104) with this b - + - - - + + + - + @@ -241,153 +225,130 @@ values to shadow registers corresponding to OTP words (95 & 104) with this b - + - - - + + + - + - + - + - + - + - + - + - + - - + + - - - - - - - + + + + + + + - - + + - - - - + + + + - - - - + + + + - - - - - + + + + + - + - + - + - - + + - - + + - + - + - - - + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/debuggers/debug_probe.py b/spsdk/debuggers/debug_probe.py index fabd2c6e..4384c384 100644 --- a/spsdk/debuggers/debug_probe.py +++ b/spsdk/debuggers/debug_probe.py @@ -56,9 +56,9 @@ def __init__(self, hardware_id: str, user_params: Dict = None) -> None: def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) -> list: """Functions returns the list of all connected probes in system. - There is option to look for just for one debug porbe defined by its hardware ID. + There is option to look for just for one debug probe defined by its hardware ID. - :param hardware_id: None to list all probes, otherwice the the only probe with + :param hardware_id: None to list all probes, otherwise the the only probe with matching hardware id is listed. :param user_params: The user params dictionary :return: ProbeDescription @@ -180,7 +180,7 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: It reads coresight register function for SPSDK library to support various DEBUG PROBES. - :param access_port: if True, the Access Port (AP) register will be read(defau1lt), otherwise the Debug Port + :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) :raises NotImplementedError: The coresight_reg_read is NOT implemented diff --git a/spsdk/debuggers/debug_probe_jlink.py b/spsdk/debuggers/debug_probe_jlink.py index c61e39bd..35a9581d 100644 --- a/spsdk/debuggers/debug_probe_jlink.py +++ b/spsdk/debuggers/debug_probe_jlink.py @@ -78,7 +78,7 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) :param user_params: The user params dictionary :return: probe_description """ - #TODO fix problems with cyclic import + #pylint: disable=import-outside-toplevel from .utils import DebugProbes, ProbeDescription jlink = DebugProbePyLink.get_jlink_lib() @@ -114,15 +114,15 @@ def open(self) -> None: self.pylink.open(serial_no=self.hardware_id, ip_addr=self.user_params.get("ip_address")) self.pylink.set_tif(pylink.enums.JLinkInterfaces.SWD) self.pylink.coresight_configure() - debugmb_ap_ix = self._get_dmbox_ap() + debug_mbox_ap_ix = self._get_dmbox_ap() # Select ISP - AP if self.dbgmlbx_ap_ix == -1: - if debugmb_ap_ix == -1: + if debug_mbox_ap_ix == -1: raise DebugProbeError(f"The Debug mailbox access port is not available!") - self.dbgmlbx_ap_ix = debugmb_ap_ix + self.dbgmlbx_ap_ix = debug_mbox_ap_ix else: - if debugmb_ap_ix != self.dbgmlbx_ap_ix: + if debug_mbox_ap_ix != self.dbgmlbx_ap_ix: logger.info(f"The detected debug mailbox accessport index is different to specified.") self._select_ap(ap_ix=self.dbgmlbx_ap_ix) @@ -232,7 +232,7 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: The PyLink read coresight register function for SPSDK library to support various DEBUG PROBES. - :param access_port: if True, the Access Port (AP) register will be read(defau1lt), otherwise the Debug Port + :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) :raises DebugProbeTransferError: The IO operation failed @@ -253,7 +253,7 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: request = swd.ReadRequest(addr // 4, ap=access_port) response = request.send(self.pylink) if access_port: - sleep(0.1) #TODO Check if this delay is necessary + sleep(0.1) request2 = swd.ReadRequest(3, ap=False) response2 = request2.send(self.pylink) return response2.data @@ -308,7 +308,7 @@ def reset(self) -> None: self.pylink.reset() def _select_ap(self, ap_ix: int, address: int = 0) -> None: - """Helper function to selct the access port in DP. + """Helper function to select the access port in DP. :param ap_ix: requested Access port index. :param address: requested address. diff --git a/spsdk/debuggers/debug_probe_pemicro.py b/spsdk/debuggers/debug_probe_pemicro.py index cc4eaf18..40911d4c 100644 --- a/spsdk/debuggers/debug_probe_pemicro.py +++ b/spsdk/debuggers/debug_probe_pemicro.py @@ -55,12 +55,12 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) This functions returns the list of all connected probes in system by Pemicro package. - :param hardware_id: None to list all probes, otherwice the the only probe with matching + :param hardware_id: None to list all probes, otherwise the the only probe with matching hardware id is listed. :param user_params: The user params dictionary :return: probe_description """ - #TODO fix problems with cyclic import + #pylint: disable=import-outside-toplevel from .utils import DebugProbes, ProbeDescription pemicro = DebugProbePemicro.get_pemicro_lib() @@ -93,16 +93,16 @@ def open(self) -> None: try: self.pemicro.open(debug_hardware_name_ip_or_serialnum=self.hardware_id) self.pemicro.connect(PEMicroInterfaces.SWD) # type: ignore - debugmb_dbgmlbx_ap_ix = self._get_dmbox_ap() + dbgmlbx_ap_ix = self._get_dmbox_ap() except PEMicroException as exc: raise DebugProbeError(f"Pemicro cannot establish communication with target({str(exc)}).") if self.dbgmlbx_ap_ix == -1: - if debugmb_dbgmlbx_ap_ix == -1: + if dbgmlbx_ap_ix == -1: raise DebugProbeError(f"The Debug mailbox access port is not available!") - self.dbgmlbx_ap_ix = debugmb_dbgmlbx_ap_ix + self.dbgmlbx_ap_ix = dbgmlbx_ap_ix else: - if debugmb_dbgmlbx_ap_ix != self.dbgmlbx_ap_ix: + if dbgmlbx_ap_ix != self.dbgmlbx_ap_ix: logger.info(f"The detected debug mailbox accessport index is different to specified.") def close(self) -> None: @@ -249,7 +249,7 @@ def reset(self) -> None: try: self.pemicro.reset_target() except PEMicroException as exc: - logger.warning(f"The reset sequence occured some errors.") + logger.warning(f"The reset sequence occurred some errors.") self.pemicro.control_reset_line(assert_reset=False) def _get_dmbox_ap(self) -> int: diff --git a/spsdk/debuggers/debug_probe_pyocd.py b/spsdk/debuggers/debug_probe_pyocd.py index f1ae333f..3b229b24 100644 --- a/spsdk/debuggers/debug_probe_pyocd.py +++ b/spsdk/debuggers/debug_probe_pyocd.py @@ -81,11 +81,12 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) This functions returns the list of all connected probes in system by PyOCD package. - :param hardware_id: None to list all probes, otherwice the the only probe with matching + :param hardware_id: None to list all probes, otherwise the the only probe with matching hardware id is listed. :param user_params: The user params dictionary :return: probe_description """ + #pylint: disable=import-outside-toplevel from .utils import DebugProbes, ProbeDescription probes = DebugProbes() @@ -232,7 +233,7 @@ def _get_ap_by_ix(self, index: int) -> Any: if access_port.address.apsel == index: return access_port - raise DebugProbeError(f"The accees port {index} is not present.") + raise DebugProbeError(f"The access port {index} is not present.") def _get_ap_by_addr(self, addr: int) -> Any: """Function returns the AP PyoCD object by address if exists. @@ -423,12 +424,12 @@ def _connect_jlink(self) -> None: # Convert protocol to port enum. if protocol == PyOCDDebugProbe.Protocol.SWD: - iface = pylink.enums.JLinkInterfaces.SWD + interface = pylink.enums.JLinkInterfaces.SWD elif protocol == PyOCDDebugProbe.Protocol.JTAG: - iface = pylink.enums.JLinkInterfaces.JTAG + interface = pylink.enums.JLinkInterfaces.JTAG try: - probe._link.set_tif(iface) + probe._link.set_tif(interface) if probe.session.options.get('jlink.power'): probe._link.power_on() #device_name = probe.session.options.get('jlink.device') or "Cortex-M4" diff --git a/spsdk/debuggers/utils.py b/spsdk/debuggers/utils.py index cc73eda8..4d5032d9 100644 --- a/spsdk/debuggers/utils.py +++ b/spsdk/debuggers/utils.py @@ -29,7 +29,7 @@ class ProbeDescription(): """NamedTuple for DAT record of debug probe description.""" def __init__(self, interface: str, hardware_id: str, description: str, probe: Type[DebugProbe]) -> None: - """Initialization of Debug probe dscription class. + """Initialization of Debug probe description class. param interface: Probe Interface. param hardware_id: Probe Hardware ID(Identification). @@ -78,7 +78,7 @@ def insert(self, index: int, item: ProbeDescription) -> None: def select_probe(self, silent: bool = False) -> ProbeDescription: """Perform Probe selection. - :param silent: When it True, the functions selct the probe if applicable without any prints to log + :param silent: When it True, the functions select the probe if applicable without any prints to log :return: The record of selected DebugProbe :raises ProbeNotFoundError: No probe has been founded """ @@ -87,18 +87,18 @@ def select_probe(self, silent: bool = False) -> ProbeDescription: print("There is no any debug probe connected in system!") raise ProbeNotFoundError("There is no any debug probe connected in system!") - if not silent or len(self) > 1: + if not silent or len(self) > 1: # pragma: no cover self.print() if len(self) == 1: # Automatically gets and use only one option\ i_selected = 0 - else: + else: # pragma: no cover print("Please choose the debug probe: ", end='') i_selected = int(input()) if i_selected > len(self)-1: - print("The choosen probe index is out of range") - raise ProbeNotFoundError("The choosen probe index is out of range") + print("The chosen probe index is out of range") + raise ProbeNotFoundError("The chosen probe index is out of range") return self[i_selected] @@ -137,8 +137,8 @@ def get_connected_probes(interface: str = None, hardware_id: str = None, user_pa The caller could restrict the scanned interfaces by specification of hardware ID. - :param interface: None to scan all interfaces, otherwice the selected interface is scanned only. - :param hardware_id: None to list all probes, otherwice the the only probe with matching + :param interface: None to scan all interfaces, otherwise the selected interface is scanned only. + :param hardware_id: None to list all probes, otherwise the the only probe with matching :param user_params: The dictionary with optional user parameters hardware id is listed. :return: list of probe_description's @@ -152,23 +152,3 @@ def get_connected_probes(interface: str = None, hardware_id: str = None, user_pa logger.warning(f"The {probe_key} debug probe support is not ready({str(exc)}).") return probes - - @staticmethod - def get_probe(interface: str = None, hardware_id: str = None, user_params: Dict = None) -> DebugProbe: - """Function returns the instance of the debug probe by input identicication ID's. - - If the Hardware ID is not specified, the first in the list iis returned. If no probe is found in system - the function returns None. - :param interface: None to scan all interfaces, otherwice the selected interface is scanned only. - :param hardware_id: None to list all probes, otherwice the the only probe with matching - hardware id is listed. - :param user_params: The dictionary with optional user parameters - :return: instance of DebugProbe - :raises ProbeNotFoundError: No probe has been founded - """ - probes = DebugProbeUtils.get_connected_probes(interface=interface, hardware_id=hardware_id) - - if len(probes) > 0: - return probes[0].probe(hardware_id=hardware_id, user_params=user_params) - - raise ProbeNotFoundError("The choosen probe index is out of range") diff --git a/spsdk/image/mbimg.py b/spsdk/image/mbimg.py index da709e3f..86ec3f26 100644 --- a/spsdk/image/mbimg.py +++ b/spsdk/image/mbimg.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2020 NXP +# Copyright 2019-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -11,7 +11,7 @@ from typing import Any, List, Optional, Sequence, Union from Crypto.Cipher import AES -from crccheck.crc import Crc32Mpeg2 +from crcmod.predefined import mkPredefinedCrcFun from spsdk.crypto import SignatureProvider from spsdk.image.keystore import KeySourceType, KeyStore @@ -153,6 +153,7 @@ class MultipleImageTable: It can be used for multicore images (one image for each core) or trustzone images (merging secure and non-secure image) """ + def __init__(self) -> None: """Initialize the Multiple Image Table.""" self._entries: List[MultipleImageEntry] = list() @@ -333,8 +334,8 @@ def __init__(self, app: Union[bytes, bytearray], # security stuff self.cert_block = cert_block if self.cert_block: - self.cert_block.alignment = 4 #type: ignore # this value is used by elf-to-sb-gui - self.signature_len = self.cert_block.signature_size #type: ignore + self.cert_block.alignment = 4 # type: ignore # this value is used by elf-to-sb-gui + self.signature_len = self.cert_block.signature_size # type: ignore else: self.signature_len = 0 self._priv_key_pem_data = priv_key_pem_data @@ -394,7 +395,7 @@ def _verify_private_key(self) -> None: if self._priv_key_pem_data: cert_blk = self.cert_block assert cert_blk is not None - if not cert_blk.verify_private_key(self._priv_key_pem_data): #type: ignore + if not cert_blk.verify_private_key(self._priv_key_pem_data): # type: ignore raise ValueError('Signature verification failed, private key does not match to certificate') def info(self) -> str: @@ -435,8 +436,9 @@ def _update_ivt(self, data: bytes) -> bytes: if MasterBootImageType.has_crc(self.image_type): # calculate CRC using MPEG2 specification over all of data (app and trustzone) # expect for 4 bytes at CRC_BLOCK_OFFSET and put the resulting CRC there - crc = Crc32Mpeg2.calc(data[:self.CRC_BLOCK_OFFSET]) - crc = Crc32Mpeg2.calc(data[self.CRC_BLOCK_OFFSET + 4:], crc) + crc32_function = mkPredefinedCrcFun('crc-32-mpeg') + crc = crc32_function(data[:self.CRC_BLOCK_OFFSET]) + crc = crc32_function(data[self.CRC_BLOCK_OFFSET + 4:], crc) data[self.CRC_BLOCK_OFFSET: self.CRC_BLOCK_OFFSET + 4] = struct.pack(" bytes: encr_header = encr_data[:56] + self.ctr_init_vector else: encr_header = bytes() - self.cert_block.image_length = len(encr_data) + len(self.cert_block.export()) + len(encr_header) #type: ignore + self.cert_block.image_length = len(encr_data) + len(self.cert_block.export()) + len(encr_header) # type: ignore return self.cert_block.export() + encr_header def _hmac(self, data: bytes) -> bytes: diff --git a/spsdk/image/misc.py b/spsdk/image/misc.py index 82a7b070..afba54d5 100644 --- a/spsdk/image/misc.py +++ b/spsdk/image/misc.py @@ -12,6 +12,7 @@ from io import SEEK_CUR from typing import Union +from spsdk.utils.registers import value_to_int from .header import Header from .. import SPSDKError @@ -80,7 +81,7 @@ def read_raw_data(stream: Union[io.BufferedReader, io.BytesIO], length: int, ind def read_raw_segment(buffer: Union[io.BufferedReader, io.BytesIO], segment_tag: int, index: int = None) -> bytes: - """Read raw segmement.""" + """Read raw segment.""" hrdata = read_raw_data(buffer, Header.SIZE, index) length = Header.parse(hrdata, 0, segment_tag).length - Header.SIZE return hrdata + read_raw_data(buffer, length) @@ -106,20 +107,6 @@ def parse_int(number: str) -> int: }[match.group('prefix')] return int(match.group('number'), base=base) - -def format_value(value: int, size: int) -> str: - """Convert the 'value' into either BIN or HEX string, depending on 'size'. - - if 'size' is divisible by 8, function returns HEX, BIN otherwise - digits in result string are grouped by 4 using '_' (underscore) - """ - padding = size if size % 8 else (size // 8) * 2 - infix = 'b' if size % 8 else 'x' - parts = re.findall(".{1,4}", f"{value:0{padding}{infix}}"[::-1]) - rev = "_".join(parts)[::-1] - return f"0{infix}{rev}" - - def dict_diff(main: dict, mod: dict) -> dict: """Return a difference between two dictionaries if key is not present in main, it's skipped.""" diff = {} @@ -132,6 +119,11 @@ def dict_diff(main: dict, mod: dict) -> dict: if key not in main: continue main_value = main[key] if isinstance(main, dict) else main - if parse_int(main_value) != parse_int(value): - diff[key] = value + try: + if value_to_int(main_value) != value_to_int(value): + diff[key] = value + except TypeError: + # Not a number! + if main_value != value: + diff[key] = value return diff diff --git a/spsdk/image/trustzone.py b/spsdk/image/trustzone.py index 6348cbce..8611030e 100644 --- a/spsdk/image/trustzone.py +++ b/spsdk/image/trustzone.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -13,8 +13,9 @@ from typing import Optional from spsdk.utils.easy_enum import Enum +from spsdk.utils.misc import format_value -from .misc import parse_int, format_value +from .misc import parse_int class TrustZoneType(Enum): diff --git a/spsdk/mboot/commands.py b/spsdk/mboot/commands.py index e809c770..ac476c80 100644 --- a/spsdk/mboot/commands.py +++ b/spsdk/mboot/commands.py @@ -19,6 +19,7 @@ # McuBoot Commands and Responses Tags ######################################################################################################################## + class CommandTag(Enum): """McuBoot Commands.""" @@ -75,13 +76,13 @@ class KeyProvOperation(Enum): class KeyProvUserKeyType(Enum): """Enumeration of supported user keys in PUF. Keys are SoC specific, not all will be supported for the processor.""" - OTFADKEK = (2, "Key for OTFAD encryption") # used on RTxxx - SBKEK = (3, "Key for SB file encryption") # Available on LPC55Sxx and RTxxx - PRINCE_REGION_0 = (7, "TODO description") # LPC55Sxx - PRINCE_REGION_1 = (8, "TODO description") # LPC55Sxx - PRINCE_REGION_2 = (9, "TODO description") # LPC55Sxx - USERKEK = (11, "Encrypted boot image key") # LPC55Sxx and RTxxx - UDS = (12, "TODO description") # LPC55Sxx and RTxxx + OTFADKEK = (2, "OTFADKEK", "Key for OTFAD encryption") # used on RTxxx + SBKEK = (3, "SBKEK", "Key for SB file encryption") # Available on LPC55Sxx and RTxxx + PRINCE_REGION_0 = (7, "PRINCE0", "Key for Prince region 0") # LPC55Sxx + PRINCE_REGION_1 = (8, "PRINCE1", "Key for Prince region 1") # LPC55Sxx + PRINCE_REGION_2 = (9, "PRINCE2", "Key for Prince region 2") # LPC55Sxx + USERKEK = (11, "USERKEK", "Encrypted boot image key") # LPC55Sxx and RTxxx + UDS = (12, "UDS", "Universal Device Secret for DICE") # LPC55Sxx and RTxxx class GenerateKeyBlobSelect(Enum): @@ -350,6 +351,19 @@ def info(self) -> str: return f"Tag={tag}, Status={status}, Length={self.length}" +class NoResponse(CmdResponse): + """Special internal case when no response is provided by the target.""" + + def __init__(self, cmd_tag: int) -> None: + """Create a NoResponse to an command that was issued, indicated by its tag. + + :param cmd_tag: Tag of the command that preceded the no-response from target + """ + header = CmdHeader(tag=cmd_tag, flags=0, reserved=0, params_count=0) + raw_data = pack(' CmdResponse: """Parse command response. diff --git a/spsdk/mboot/error_codes.py b/spsdk/mboot/error_codes.py index f393e12c..17dbd5df 100644 --- a/spsdk/mboot/error_codes.py +++ b/spsdk/mboot/error_codes.py @@ -84,7 +84,7 @@ class StatusCode(Enum): SECURITY_VIOLATION = (10001, 'SecurityViolation', 'Security Violation') ABORT_DATA_PHASE = (10002, 'AbortDataPhase', 'Abort Data Phase') PING_ERROR = (10003, 'PingError', 'Ping Error') - NO_RESPONSE = (10004, 'NoResponse', 'No Response') + NO_RESPONSE = (10004, 'NoResponse', 'No response packet from target device') NO_RESPONSE_EXPECTED = (10005, 'NoResponseExpected', 'No Response Expected') UNSUPPORTED_COMMAND = (10006, 'UnsupportedCommand', 'Unsupported Command') diff --git a/spsdk/mboot/interfaces/uart.py b/spsdk/mboot/interfaces/uart.py index 6f23d950..f8bf8351 100644 --- a/spsdk/mboot/interfaces/uart.py +++ b/spsdk/mboot/interfaces/uart.py @@ -13,7 +13,7 @@ from typing import List, Optional, Tuple, Union import construct -from crccheck.crc import Crc16 +from crcmod.predefined import mkPredefinedCrcFun from serial import Serial, SerialException from serial.tools.list_ports import comports @@ -26,7 +26,7 @@ logger = logging.getLogger("MBOOT:UART") -def scan_uart(port: str = None, baudrate: int = 57600, timeout: int = 5000) -> List[Interface]: +def scan_uart(port: str = None, baudrate: int = None, timeout: int = None) -> List[Interface]: """Scan connected serial ports. Returns list of serial ports with devices that respond to PING command. @@ -35,10 +35,12 @@ def scan_uart(port: str = None, baudrate: int = 57600, timeout: int = 5000) -> L :param port: name of preferred serial port, defaults to None :param baudrate: speed of the UART interface, defaults to 56700 - :param timeout: timeout in milliseconds + :param timeout: timeout in milliseconds, defaults to 5000 :return: list of interfaces responding to the PING command :rtype: List[spsdk.mboot.interfaces.base.Interface] """ + baudrate = baudrate or 57600 + timeout = timeout or 5000 if port: interface = _check_port(port, baudrate, timeout) return [interface] if interface else [] @@ -56,6 +58,7 @@ def _check_port(port: str, baudrate: int, timeout: int) -> Optional[Interface]: :rtype: Optional[Interface] """ try: + logger.debug(f'Checking port: {port}, baudrate: {baudrate}, timeout: {timeout}') interface = Uart(port=port, baudrate=baudrate, timeout=timeout) interface.open() interface.close() @@ -73,7 +76,8 @@ def calc_crc(data: bytes) -> int: :return: calculated CRC :rtype: int """ - return Crc16.calc(data) + crc_function = mkPredefinedCrcFun('xmodem') + return crc_function(data) def to_int(data: bytes, little_endian: bool = True) -> int: diff --git a/spsdk/mboot/interfaces/usb.py b/spsdk/mboot/interfaces/usb.py index 7d69240c..173a4c17 100644 --- a/spsdk/mboot/interfaces/usb.py +++ b/spsdk/mboot/interfaces/usb.py @@ -191,12 +191,16 @@ def read(self) -> Union[CmdResponse, bytes]: :return: Return CmdResponse object. :raises McuBootConnectionError: Raises an error if device is not openned for reading + :raises TimeoutError: Time-out """ if not self.is_opened: raise McuBootConnectionError(f"Device is not openned for reading") assert self.device raw_data = self.device.read(1024, self.timeout) + if not raw_data: + logger.error(self.device.error()) + raise TimeoutError() # NOTE: uncomment the following when using KBoot/Flashloader v2.1 and older # import platform # if platform.system() == "Linux": diff --git a/spsdk/mboot/mcuboot.py b/spsdk/mboot/mcuboot.py index 9fc0c1a7..a2d32337 100644 --- a/spsdk/mboot/mcuboot.py +++ b/spsdk/mboot/mcuboot.py @@ -15,7 +15,7 @@ from .commands import ( CmdResponse, CommandTag, KeyProvOperation, KeyProvUserKeyType, - CmdPacket, GenericResponse, GenerateKeyBlobSelect + CmdPacket, GenericResponse, GenerateKeyBlobSelect, NoResponse ) from .error_codes import StatusCode from .exceptions import McuBootCommandError, McuBootConnectionError, SPSDKError, McuBootDataAbortError @@ -85,7 +85,7 @@ def _process_cmd(self, cmd_packet: CmdPacket) -> Any: except TimeoutError: self._status_code = StatusCode.NO_RESPONSE logger.debug('RX-PACKET: No Response, Timeout Error !') - raise McuBootConnectionError("No Response from Device") + response = NoResponse(cmd_tag=cmd_packet.header.tag) assert isinstance(response, CmdResponse) logger.debug(f'RX-PACKET: {response.info()}') @@ -120,7 +120,8 @@ def _read_data(self, cmd_tag: int, length: int) -> bytes: except TimeoutError: self._status_code = StatusCode.NO_RESPONSE logger.debug('RX: No Response, Timeout Error !') - raise McuBootConnectionError("No Response from Device") + response = NoResponse(cmd_tag=cmd_tag) + break if isinstance(response, bytes): data += response diff --git a/spsdk/pfr/__init__.py b/spsdk/pfr/__init__.py index 87483118..bdcba97b 100644 --- a/spsdk/pfr/__init__.py +++ b/spsdk/pfr/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -13,6 +13,14 @@ PFR_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, 'pfr') -from .pfr import CMPA, CFPA +from .pfr import ( + CMPA, + CFPA, + PfrConfiguration +) +from .exceptions import ( + SPSDKPfrConfigError, + SPSDKPfrConfigReadError +) from .translator import Translator from .processor import Processor diff --git a/spsdk/pfr/exceptions.py b/spsdk/pfr/exceptions.py new file mode 100644 index 00000000..693ecdf6 --- /dev/null +++ b/spsdk/pfr/exceptions.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Module provides exceptions for PFR module.""" +from spsdk import SPSDKError + +class SPSDKPfrError(SPSDKError): + """General PFR error.""" + +class SPSDKPfrConfigError(SPSDKPfrError): + """General PFR configuration error.""" + +class SPSDKPfrConfigReadError(SPSDKPfrConfigError): + """Configuration file decode error.""" + +class SPSDKPfrRotkhIsNotPresent(SPSDKPfrError): + """The configuration area doesn't provide ROTKH field.""" diff --git a/spsdk/pfr/pfr.py b/spsdk/pfr/pfr.py index 7224dd58..46182abe 100644 --- a/spsdk/pfr/pfr.py +++ b/spsdk/pfr/pfr.py @@ -1,307 +1,456 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Module provides support for Protected Flash Region areas (CMPA, CFPA).""" -import json +import logging import math import os -from typing import List, Union -from xml.etree import ElementTree as ET +import copy +from typing import List, Union, Any +import json +import yaml +from ruamel.yaml.comments import CommentedMap as CM -from bitstring import BitArray # type: ignore #Type info for bitstring is not available from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -from spsdk.image.misc import format_value, parse_int +from spsdk import __version__ as spsdk_version +from spsdk import __release__ as spsdk_release +from spsdk import __author__ as spsdk_author from spsdk.utils.crypto.abstract import BackendClass from spsdk.utils.crypto.backend_openssl import openssl_backend +from spsdk.utils.registers import Registers, RegsBitField, RegsRegister +from spsdk.utils.reg_config import RegConfig +from spsdk.utils.misc import change_endianism, reverse_bytes_in_longs, value_to_int, format_value +from spsdk.utils.exceptions import SPSDKRegsErrorRegisterNotFound from . import PFR_DATA_FOLDER +from .exceptions import ( + SPSDKPfrError, + SPSDKPfrConfigError, + SPSDKPfrConfigReadError, + SPSDKPfrRotkhIsNotPresent +) + +logger = logging.getLogger(__name__) + +class PfrConfiguration(): + """Class to open PFR configuration file a get basic configuration.""" + + def __init__(self, file_name: str = None) -> None: + """Open config PFR file. + + :param file_name: File name of PFR configuration. + :raises SPSDKPfrConfigReadError: Invalid configuration file. + """ + self.device = "Unknown" + self.revision = "latest" + self.type = "Unknown" + self.file_type = "Unknown" + self.settings = None + + if file_name: + self.set_config(file_name) + + def set_config_dict(self, data: Union[CM, dict]) -> None: + """Apply configuration dictionary. + + The function accepts as dictionary as from commented map. + + :param data: Commented map of YML configuration. + :raises SPSDKPfrConfigReadError: Invalid YML file. + """ + if data is None or len(data) == 0: + raise SPSDKPfrConfigReadError(f"Empty YAML configuration.") + + try: + description = data.get("description", data) + self.device = description["device"].lower() or "" + self.revision = description.get("revision", "latest").lower() + self.type = description["type"] + self.settings = data["settings"] + self.file_type = "YAML" if isinstance(data, CM) else "JSON" + except KeyError: + raise SPSDKPfrConfigReadError(f"Missing fields in YAML configuration.") + + def set_config_json(self, file_name: str) -> None: + """Apply JSON configuration from file. + + :param file_name: Name of JSON configuration file. + :raises SPSDKPfrConfigReadError: Invalid JSON file. + """ + try: + with open(file_name, "r") as file_json: + data = json.load(file_json) + except (FileNotFoundError, TypeError, ValueError) as exc: + raise SPSDKPfrConfigReadError(f"Cannot load JSON configuration file. ({file_name}) - {exc}") + + try: + self.set_config_dict(data) + self.file_type = "JSON" + except SPSDKPfrConfigReadError as exc: + raise SPSDKPfrConfigReadError(f"Decoding error({str(exc)}) with JSON configuration file. ({file_name})") + + def set_config_yml(self, file_name: str) -> None: + """Apply YML configuration from file. + + :param file_name: Name of YML configuration file. + :raises SPSDKPfrConfigReadError: Invalid YML commented map. + """ + try: + with open(file_name, "r") as file_yml: + yml_raw = file_yml.read() + data = yaml.safe_load(yml_raw) + except (FileNotFoundError, TypeError, ValueError) as exc: + raise SPSDKPfrConfigReadError(f"Cannot load YAML configuration file. ({file_name}) - {exc}") + + try: + self.set_config_dict(data) + self.file_type = "YAML" + except SPSDKPfrConfigReadError as exc: + raise SPSDKPfrConfigReadError(f"Decoding error({str(exc)}) with YAML configuration file. ({file_name})") + + def set_config(self, config: Union[str, CM, dict]) -> None: + """Apply configuration from file. + + :param config: Name of configuration file or Commented map. + """ + if isinstance(config, (CM, dict)): + self.set_config_dict(config) + else: + extension = os.path.splitext(config)[1] + # Try open configuration file by its extensions + if extension == ".json": + self.set_config_json(config) + elif extension in (".yml", ".yaml"): + self.set_config_yml(config) + else: + # Just try to open one by one to be lucky + try: + self.set_config_json(config) + except SPSDKPfrConfigReadError: + self.set_config_yml(config) + + def get_yaml_config(self, data: CM, indent: int = 0) -> CM: + """Return YAML configuration In PfrConfiguration format. + + :param data: The registers settings data. + :param indent: YAML start indent. + :return: YAML PFR configuration in commented map(ordered dict). + """ + res_data = CM() + + res_data.yaml_set_start_comment(f"NXP {self.device.upper()} PFR {self.type} configuration", indent=indent) + + description = CM() + description.insert(1, "device", self.device, comment="The NXP device name.") + description.insert(2, "revision", self.revision, comment="The NXP device revision.") + description.insert(3, "type", self.type.upper(), comment="The PFR type (CMPA, CFPA).") + description.insert(4, "version", spsdk_version, comment="The SPSDK tool version.") + description.insert(5, "author", spsdk_author, comment="The author of the configuration.") + description.insert(6, "release", spsdk_release, comment="The SPSDK release.") + + res_data.insert(1, "description", description, comment=f"The PFR {self.type} configuration description.") + res_data.insert(2, "settings", data, comment=f"The PFR {self.type} registers configuration.") + return res_data + + def get_json_config(self, data: dict) -> dict: + """Return JSON configuration In PfrConfiguration format. + + :param data: The registers settings data. + :return: JSON PFR configuration in dictionary. + """ + res_data = {} + + description = {} + description["device"] = self.device + description["revision"] = self.revision + description["type"] = self.type.upper() + description["version"] = spsdk_version + description["author"] = spsdk_author + description["release"] = spsdk_release + + res_data["description"] = description + res_data["settings"] = data + return res_data class BaseConfigArea: """Base for CMPA and CFPA classes.""" CONFIG_DIR = PFR_DATA_FOLDER CONFIG_FILE = "database.json" BINARY_SIZE = 512 - HAS_ROTKH = True ROTKH_SIZE = 32 - ROTKH_START_REGISTER = "ROTKH0" + ROTKH_REGISTER = "ROTKH" MARK = b'SEAL' - - def __init__(self, device: str, keys: List[RSAPublicKey] = None, - revision: str = None, user_config: dict = None, - rotkh: bytes = None) -> None: + DESCRIPTION = "Base Config Area" + def __init__(self, device: str, + revision: str = None, user_config: PfrConfiguration = None) -> None: """Initialize an instance. :param device: device to use, list of supported devices is available via 'devices' method - :param keys: list of RSA Public Keys to compute ROTKH :param revision: silicon revision, if not specified, the latest is being used - :param user_config: dict with user configuration - :param rotkh: pre-computed ROTKH + :param user_config: PfrConfiguration with user configuration to use with initialization """ self.config = self._load_config() - assert device in self.get_devices(), f"Device '{device}' is not supported" + assert device in self.config.get_devices(), f"Device '{device}' is not supported" self.device = device - self.revision = revision or self._get_latest_revision(device) - assert self.revision in self.get_revisions(device), f"Invalid revision '{revision}' for '{device}'" - self.data = self._load_data() - self.user_config = user_config or dict() - self.keys = keys - self.rotkh = rotkh + self.revision = revision or self.config.get_latest_revision(device) + assert self.revision in self.config.get_revisions(device), f"Invalid revision '{revision}' for '{device}'" + self.registers = Registers(device) + self.registers.load_registers_from_xml(self.config.get_data_file(self.device, self.revision), + grouped_regs=self.config.get_grouped_registers(self.device)) + + # Set the computed field handler + for reg, fields in self.config.get_computed_fields(self.device).items(): + reg_obj = self.registers.find_reg(reg) + reg_obj.add_setvalue_hook(self.reg_computed_fields_handler, fields) + + self.user_config = PfrConfiguration() + self.user_config.device = self.device + self.user_config.revision = self.revision + self.user_config.type = self.__class__.__name__ + + if user_config: + self.user_config = user_config + self.set_config(self.user_config, raw=False) + + def reg_computed_fields_handler(self, val: bytes, context: Any) -> bytes: + """Recalculate all fields for given register value. + + :param val: Input register value. + :param context: The method context (fields). + :return: recomputed value. + :raises SPSDKPfrError: Raises when the computing routine is not found. + """ + fields: dict = context + for method in fields.values(): + if hasattr(self, method): + method_ref = getattr(self, method, None) + val = method_ref(val) + else: + raise SPSDKPfrError(f"The '{method}' compute function doesn't exists.") + + return val + + @staticmethod + def pfr_reg_inverse_high_half(val: bytes) -> bytes: + """Function that inverse low 16-bits of register value to high 16 bits. + + :param val: Input current reg value. + :return: Returns the complete register value with updated higher half field. + """ + ret = bytearray(val) + ret[0] = ret[2] ^ 0xff + ret[1] = ret[3] ^ 0xff + return bytes(ret) @classmethod - def _load_config(cls) -> dict: + def _load_config(cls) -> RegConfig: """Load config file.""" - with open(os.path.join(cls.CONFIG_DIR, cls.CONFIG_FILE)) as config_file: - return json.load(config_file) - - def _get_latest_revision(self, device: str) -> str: - """Get latest revision for device.""" - return self.config["devices"][device]["latest"] + return RegConfig(os.path.join(cls.CONFIG_DIR, cls.CONFIG_FILE)) @classmethod def devices(cls) -> List[str]: """Classmethod to get list of supported devices.""" config = cls._load_config() - return list(config['devices'].keys()) - - def get_devices(self) -> List[str]: - """Get list of supported devices.""" - return list(self.config["devices"].keys()) - - def get_revisions(self, device: str) -> List[str]: - """Get list of revisions for given device.""" - return list(self.config["devices"][device]["revisions"].keys()) - - def get_address(self, remove_underscore: bool = False) -> str: - """Get the area address in chip memory.""" - address = self.config["devices"][self.device]["address"] - if remove_underscore: - return address.replace("_", "") - return address - - def _get_data_file(self) -> str: - """Return the full path to data file (xml).""" - file_name = self.config["devices"][self.device]["revisions"][self.revision] - return os.path.join(self.CONFIG_DIR, file_name) - - def _load_data(self) -> ET.ElementTree: - """Load the register data.""" - reg_file = self._get_data_file() - return ET.parse(reg_file) - - # pylint: disable=no-self-use #It's better to have this function visually close to callies - def _filter_by_names(self, items: List[ET.Element], names: List[str]) -> List[ET.Element]: - """Filter out all items in the "items" tree,whose name starts with one of the strings in "names" list.""" - filtered = [ - item for item in items if not item.attrib["name"].startswith(tuple(names)) - ] - return filtered - - def _filter_computed_registers(self, items: List[ET.Element]) -> List[ET.Element]: - """Filter computed registers.""" - regs = self.config["computed_registers"] - return self._filter_by_names(items, regs) - - def _filter_computed_fields(self, items: List[ET.Element]) -> List[ET.Element]: - """Filter computed fields.""" - fields = self.config["computed_fields"] - return self._filter_by_names(items, fields) - - def _filter_ignored_registers(self, items: List[ET.Element]) -> List[ET.Element]: - """Filter registers that shall be ignored.""" - regs = self.config.get("ignored_registers", "") - return self._filter_by_names(items, regs) - - def _get_registers(self, exclude_computed: bool = True) -> List[ET.Element]: + return config.get_devices() + + def _get_registers(self, exclude_computed: bool = True) -> List[RegsRegister]: """Get a list of all registers as ElementTree.""" - registers = self.data.findall("register") - registers = self._filter_ignored_registers(registers) + exclude = self.config.get_ignored_registers(self.device) if exclude_computed: - return self._filter_computed_registers(registers) - return registers - - def _get_register(self, register_name: str) -> ET.Element: - """Get single register tree by the register's name.""" - reg = self.data.find(f"register[@name='{register_name}']") - assert reg, f"Register '{register_name}' wasn't found!" - return reg - - def _get_register_names(self, exclude_computed: bool = True) -> List[str]: - """Get a list of all register names.""" - registers = self._get_registers(exclude_computed) - return [r.attrib["name"] for r in registers] - - def _get_bitfields(self, register_name: str, exclude_computed: bool = True) -> List[ET.Element]: - """Get bitfields for register as ElementTree.""" - fields = self.data.findall(f"register[@name='{register_name}']/bit_field") + exclude.extend(self.config.get_computed_registers(self.device)) + return self.registers.get_registers(exclude) + + def _get_bitfields(self, register: RegsRegister, exclude_computed: bool = True) -> List[RegsBitField]: + """Get bitfields for register.""" + # In XML data there are mandatory FIELDS for registers without any fields + exclude = [] + ignore_bitfields = self.config.get_ignored_fields(self.device) + if ignore_bitfields: + exclude.extend(ignore_bitfields) if exclude_computed: - return self._filter_computed_fields(fields) - return fields - - def _get_bitfield_names(self, register_name: str, exclude_computed: bool = True) -> List[str]: - """Get a list of bitfield names for given register.""" - bit_fiels = self._get_bitfields(register_name, exclude_computed) - return [bf.attrib["name"] for bf in bit_fiels] - - def _get_bitfield_config(self, register_name: str, exclude: bool) -> dict: - """Get bitfield configuration.""" - field_config = {} - for field in self._get_bitfields(register_name, exclude): - name = field.attrib["name"] - value = format_value(int(field.attrib["reset_value"]), int(field.attrib["width"])) - field_config[name] = value - return field_config - - def generate_config(self, exclude_computed: bool = True) -> dict: - """Generate configuration structure for user configuration.""" - config = {} - for reg in self._get_register_names(exclude_computed): - field_config = self._get_bitfield_config(reg, exclude_computed) - if len(field_config) == 1 and field_config.get('FIELD'): - config[reg] = field_config.popitem()[1] - else: - config[reg] = field_config - return config - - def _export_register(self, register_name: str, compute_inverses: bool) -> bytes: - """Generate binary output for single register.""" - register = BitArray(length=32) - user_config = self.user_config.get(register_name, dict()) - inverse_present = False - for field in self._get_bitfields(register_name, exclude_computed=False): - name = field.attrib["name"] - offset = parse_int(field.attrib["offset"]) - width = parse_int(field.attrib["width"]) - # chcek whether there's a need to calculate inverse values - inverse_present |= name == "INVERSE_VALUE" - - # The configuration allows to configure the whole register with single value - if isinstance(user_config, str): - temp_value = user_config + exclude_fields = self.config.get_computed_fields(self.device) + if register.name in exclude_fields.keys(): + exclude.extend(exclude_fields[register.name].keys()) + + return register.get_bitfields(exclude) + + def set_config_json(self, data: Any, raw: bool = False) -> None: + """Apply JSON configuration from file. + + :param data: Data of JSON configuration. + :param raw: When set all (included computed fields) configuration will be applied. + :raises SPSDKPfrConfigReadError: Invalid JSON file. + """ + for reg in self._get_registers(not raw): + json_reg = data.get(reg.name, None) + if isinstance(json_reg, dict): + for bitfield in self._get_bitfields(reg, not raw): + json_bitfield = json_reg.get(bitfield.name, None) + if json_bitfield: + bitfield.set_value(json_bitfield, raw) + elif isinstance(json_reg, str): + reg.set_value(json_reg, raw) else: - temp_value = user_config.get(name) or field.attrib["reset_value"] - value = parse_int(temp_value) - # due to endianess we fill the bits from the end, therefore there's '-' in position - # pos = 0 means offset = 0, means end of the BitArray - register.overwrite(bs=BitArray(f"uint:{width}={value}"), pos=-(offset + width)) - if compute_inverses and inverse_present: - # NOTE: For now we'll assume the INVERSES are 16b long and inverts bits[15:0] - # should this change in the future a data model change is necessary - # NOTE: invert method changes bits in-place, thus we need to call it on separate object - # calling invert() after slicing doesn't work for BitArray - b_lower = register[16:] - b_lower.invert() - register.overwrite(b_lower, 0) - # swapping bytes from big endian into little - register.byteswap() - return register.bytes - - def _calc_rotkh(self) -> bytes: + logger.warning(f"Invalid configuration value for {reg.name}.") + + def set_config_yaml(self, data: Any, raw: bool = False) -> None: + """Apply YML configuration from file. + + :param data: Data of YML configuration. + :param raw: When set all (included computed fields) configuration will be applied. + :raises SPSDKPfrConfigReadError: Invalid YML file. + """ + computed_regs = [] + computed_regs.extend(self.config.get_ignored_registers(self.device)) + if not raw: + computed_regs.extend(self.config.get_computed_registers(self.device)) + computed_fields = None if raw else self.config.get_computed_fields(self.device) + + self.registers.load_yml_config(data, computed_regs, computed_fields) + if not raw: + # Just update only configured registers + exclude_hooks = list(set(self.registers.get_reg_names())-set(data.keys())) + self.registers.run_hooks(exclude_hooks) + + def set_config(self, config: PfrConfiguration, raw: bool = False) -> None: + """Apply configuration from file. + + :param config: PFR configuration. + :param raw: When set all (included computed fields) configuration will be applied. + :raises SPSDKPfrConfigError: Invalid config file. + """ + if config.device != self.device: + raise SPSDKPfrConfigError(f"Invalid device in configuration. {self.device} != {config.device}") + if config.revision == "latest": + config.revision = self.config.get_latest_revision(self.device) + if config.revision != self.revision: + raise SPSDKPfrConfigError(f"Invalid revision in configuration. {self.revision} != {config.revision}") + if config.type.upper() != self.__class__.__name__: + raise SPSDKPfrConfigError(f"Invalid configuration type. {self.__class__.__name__} != {config.type}") + if config.file_type == "JSON": + self.set_config_json(config.settings, raw) + elif config.file_type == "YAML": + self.set_config_yaml(config.settings, raw) + else: + raise SPSDKPfrConfigError(f"Unsupported type of configuration: {config.file_type}") + + def get_json_config(self, exclude_computed: bool = True) -> dict: + """Return JSON configuration from loaded registers. + + :param exclude_computed: Omit computed registers and fields. + :return: JSON PFR configuration. + """ + def _get_json_config_register(reg: RegsRegister) -> Union[str, dict]: + """Parse individual register, returns wither one 32b value or dict of bitfields.""" + bitfields = self._get_bitfields(reg, exclude_computed=False) + # exit early if we found a single 32b field + if len(bitfields) == 0: + return format_value(reg.get_int_value(), 32) + + register = {} + for field in bitfields: + register[field.name] = format_value(field.get_value(), field.width) + return register + + data = {} + for reg in self._get_registers(exclude_computed): + data[reg.name] = _get_json_config_register(reg) + + return self.user_config.get_json_config(data) + + def get_yaml_config(self, exclude_computed: bool = True, diff: bool = False, indent: int = 0) -> CM: + """Return YAML configuration from loaded registers. + + :param exclude_computed: Omit computed registers and fields. + :param diff: Get only configuration with difference value to reset state. + :param indent: YAML start indent. + :return: YAML PFR configuration in commented map(ordered dict). + """ + computed_regs = None if not exclude_computed else self.config.get_computed_registers(self.device) + computed_fields = None if not exclude_computed else self.config.get_computed_fields(self.device) + ignored_fields = self.config.get_ignored_fields(self.device) + + data = self.registers.create_yml_config(computed_regs, computed_fields, ignored_fields, diff, indent+2) + return self.user_config.get_yaml_config(data, indent) + + def generate_config(self, exclude_computed: bool = True) -> CM: + """Generate configuration structure for user configuration.""" + # Create own copy to keep self as is and get reset values by standard YML output + copy_of_self = copy.deepcopy(self) + copy_of_self.registers.reset_values() + + return copy_of_self.get_yaml_config(exclude_computed) + + def _calc_rotkh(self, keys: List[RSAPublicKey]) -> bytes: """Calculate ROTKH (Root Of Trust Key Hash).""" # the data structure use for computing final ROTKH is 4*32B long # 32B is a hash of individual keys # 4 is the max number of keys, if a key is not provided the slot is filled with '\x00' - assert self.keys, f"Key's were not set, can't compute ROTKH" - key_hashes = [calc_pub_key_hash(key, openssl_backend) for key in self.keys] + key_hashes = [calc_pub_key_hash(key, openssl_backend) for key in keys] data = [key_hashes[i] if i < len(key_hashes) else bytes(32) for i in range(4)] return openssl_backend.hash(bytearray().join(data)) - def _get_rotkh_start_address(self) -> int: - """Return the offset of the first ROTKHx register defined as ROTKH_START_REGISTER.""" - return parse_int(self._get_register(self.ROTKH_START_REGISTER).attrib["offset"]) - def _get_seal_start_address(self) -> int: - start = self.config['devices'][self.device].get('seal_start') - if start is None: - start = self.config['seal_start'] - assert start, "Can't find 'seal_start' in database.json" - return parse_int(self._get_register(start).attrib['offset']) + start = self.config.get_seal_start_address(self.device) + assert start, "Can't find 'seal_start_address' in database.json" + return self.registers.find_reg(start).offset def _get_seal_count(self) -> int: - count = self.config['devices'][self.device].get('seal_count') - if count is None: - count = self.config['seal_count'] + count = self.config.get_seal_count(self.device) assert count, "Can't find 'seal_count' in database.json" - return count + return value_to_int(count) - def export(self, add_seal: bool = False, compute_inverses: bool = False) -> bytes: + def export(self, add_seal: bool = False, keys: List[RSAPublicKey] = None) -> bytes: """Generate binary output.""" + if keys: + try: + # ROTKH may or may not be present, derived class defines its presense + rotkh_reg = self.registers.find_reg(self.ROTKH_REGISTER) + rotkh_data = self._calc_rotkh(keys) + rotkh_reg.set_value(rotkh_data, True) + except SPSDKRegsErrorRegisterNotFound: + raise SPSDKPfrRotkhIsNotPresent("This device doesn't contain ROTKH register!") + data = bytearray(self.BINARY_SIZE) for reg in self._get_registers(exclude_computed=False): - name = reg.attrib["name"] - width = parse_int(reg.attrib["width"]) - offset = parse_int(reg.attrib["offset"]) - assert width == 32, "Don't know how to handle non-32b registers" - register = self._export_register(name, compute_inverses) # rewriting 4B at the time - data[offset: offset + 4] = register - - # ROTKH may or may not be present, derived class defines its presense - if self.HAS_ROTKH: - rotkh_data = self.rotkh or self._calc_rotkh() - rothk_start = self._get_rotkh_start_address() - data[rothk_start: rothk_start + self.ROTKH_SIZE] = rotkh_data + if reg.has_group_registers(): + for grp_reg in reg.sub_regs: + val = grp_reg.get_value() if grp_reg.reverse else change_endianism(bytearray(grp_reg.get_value())) + data[grp_reg.offset: grp_reg.offset + grp_reg.width//8] = val + else: + val = reg.get_value() if reg.reverse else change_endianism(bytearray(reg.get_value())) + data[reg.offset: reg.offset + reg.width//8] = val if add_seal: seal_start = self._get_seal_start_address() - seal_cout = self._get_seal_count() - data[seal_start: seal_start + seal_cout * 4] = self.MARK * seal_cout + seal_count = self._get_seal_count() + data[seal_start: seal_start + seal_count * 4] = self.MARK * seal_count assert len(data) == self.BINARY_SIZE, f'The size of data is {len(data)}, is not equal to {self.BINARY_SIZE}' return bytes(data) - def _parse_register(self, register_name: str, data: bytes) -> Union[str, dict]: - """Parse individual register, returns wither one 32b value or dict of bitfields.""" - register = {} - bits = BitArray(data) - # data is stored in little endian, but processed in big endian - bits.byteswap() - for field in self._get_bitfields(register_name, exclude_computed=False): - width = parse_int(field.attrib["width"]) - # exit early if we found a single 32b field - if width == 32: - return format_value(bits.uint, width) - name = field.attrib["name"] - offset = parse_int(field.attrib["offset"]) - # OK, what the hell is that slicing about?! - # offset is marked from the end of the bitarray not the begging like in a list - # e.g.: ba = BitArray('0b00001100'), we want to extract bitfields of width=2 and offset=2 ('11') - # again offset=2 means 2 bits from the end - # BitArray supports negative indexing like an regular python list does: last bit has index -1 - # that means we want to extract bits with indecies -4,-3 => [-4:-2] - # HOWEVER: if we would want to extract 2 bits in the end (offset=0) - # we would need to use [-offset:] slice or [-offset:None] - slice_end = None if offset == 0 else -offset - filed_bits = bits[-(offset + width): slice_end] - register[name] = format_value(filed_bits.uint, width) - return register - - def parse(self, data: bytes, exclude_computed: bool = True) -> dict: - """Return a user config JSON object based on input data.""" - user_config = {} + def parse(self, data: bytes, exclude_computed: bool = True) -> None: + """Parse input binary data to registers.""" for reg in self._get_registers(exclude_computed=exclude_computed): - name = reg.attrib["name"] - width = parse_int(reg.attrib["width"]) - offset = parse_int(reg.attrib["offset"]) - assert width == 32, "Don't know how to handle non-32b registers" - reg_config = self._parse_register(name, data[offset: offset + 4]) - user_config[name] = reg_config - return user_config - + value = bytearray(data[reg.offset: reg.offset + reg.width // 8]) + reg.set_value(change_endianism(value), raw=not exclude_computed) class CMPA(BaseConfigArea): """Customer Manufacturing Configuration Area.""" CONFIG_DIR = os.path.join(BaseConfigArea.CONFIG_DIR, "cmpa") + DESCRIPTION = "Customer Manufacturing Programable Area" class CFPA(BaseConfigArea): """Customer In-Field Configuration Area.""" CONFIG_DIR = os.path.join(BaseConfigArea.CONFIG_DIR, "cfpa") - HAS_ROTKH = False + DESCRIPTION = "Customer In-field Programmable Area" def calc_pub_key_hash(public_key: RSAPublicKey, backend: BackendClass = openssl_backend) -> bytes: diff --git a/spsdk/pfr/processor.py b/spsdk/pfr/processor.py index 07ca3cab..c6473e8b 100644 --- a/spsdk/pfr/processor.py +++ b/spsdk/pfr/processor.py @@ -62,17 +62,17 @@ def process(self, condition: str) -> Tuple[bool, str]: :param condition: condition to quantify :return: Boolean result and values for translated keys """ - self.logger.debug("Transforming condition: {}".format(condition)) + self.logger.debug(f"Transforming condition: {condition}") org_node = ast.parse(condition, mode="eval") new_node = self.transformer.visit(org_node) - self.logger.debug("Transformed condition: {}".format(new_node)) + self.logger.debug(f"Transformed condition: {new_node}") node_str = astunparse.unparse(new_node) - node_str = self._replaceIntAsHex(node_str) - result = eval(compile(new_node, filename="", mode="eval")) + node_str = self._replace_int_as_hex(node_str) + result = eval(compile(new_node, filename="", mode="eval")) #pylint: disable=eval-used return result, node_str @staticmethod - def _replaceIntAsHex(string: str) -> str: + def _replace_int_as_hex(string: str) -> str: """Converts all numeric occurrences in `string` in decimal form into hexadecimal form. :param string: string to process diff --git a/spsdk/pfr/translator.py b/spsdk/pfr/translator.py index 0beca434..e3793999 100644 --- a/spsdk/pfr/translator.py +++ b/spsdk/pfr/translator.py @@ -8,30 +8,28 @@ """Translator is responsible for converting stringified keys into values.""" import logging - -import jmespath -from .pfr import CFPA, CMPA +from .pfr import CFPA, CMPA, PfrConfiguration class Translator: """Translates single strings (register/key names) into values.""" - def __init__(self, cmpa_data: dict, cfpa_data: dict) -> None: + def __init__(self, cmpa: PfrConfiguration, cfpa: PfrConfiguration) -> None: """Initialize CMPA and CFPA data. - :param cmpa_data: data loaded from CMPA json config file - :param cfpa_data: data loaded from CFPA json config file + :param cmpa: configuration data loaded from CMPA config file + :param cfpa: configuration data loaded from CFPA config file """ self.logger = logging.getLogger("translator") - self.cmpa_data = cmpa_data + self.cmpa_cfg = cmpa self.cmpa_obj = CMPA( - device=cmpa_data['device'], revision=cmpa_data['revision'], - user_config=cmpa_data['settings'] + device=cmpa.device, revision=cmpa.revision, + user_config=cmpa ) - self.cfpa_data = cfpa_data + self.cfpa_cfg = cfpa self.cfpa_obj = CFPA( - device=cfpa_data['device'], revision=cfpa_data['revision'], - user_config=cfpa_data['settings'] + device=cfpa.device, revision=cfpa.revision, + user_config=cfpa ) self.handlers = { 'CMPA': self._cmpa_translate, @@ -53,13 +51,12 @@ def translate(self, key: str) -> int: def _cmpa_translate(self, key: str) -> int: """Handler for CMPA data.""" self.logger.debug(f'Extracting value from {key}') - value = jmespath.search(key, self.cmpa_data['settings']) - if isinstance(value, dict): - reg_bytes = self.cmpa_obj._export_register(register_name=key, compute_inverses=False) - reg_val = int.from_bytes(bytes=reg_bytes, byteorder="little") - value = reg_val + splitted = key.split('.', maxsplit=1) + register = self.cmpa_obj.registers.find_reg(splitted[0]) + if len(splitted) == 2: + value = register.find_bitfield(splitted[1]).get_value() else: - value = int(value, 0) + value = register.get_int_value() self.logger.debug(f"Extracted value {value:x}") return value @@ -67,14 +64,12 @@ def _cmpa_translate(self, key: str) -> int: def _cfpa_translate(self, key: str) -> int: """Handler for CFPA data.""" self.logger.debug(f'Extracting value from {key}') - - value = jmespath.search(key, self.cfpa_data['settings']) - if isinstance(value, dict): - reg_bytes = self.cfpa_obj._export_register(register_name=key, compute_inverses=False) - reg_val = int.from_bytes(bytes=reg_bytes, byteorder="little") - value = reg_val + splitted = key.split('.', maxsplit=1) + register = self.cfpa_obj.registers.find_reg(splitted[0]) + if len(splitted) == 2: + value = register.find_bitfield(splitted[1]).get_value() else: - value = int(value, 0) + value = register.get_int_value() self.logger.debug(f"Extracted value {value:x}") return value @@ -82,6 +77,6 @@ def _cfpa_translate(self, key: str) -> int: def _util_translate(self, key: str) -> int: """Handler for Utils data.""" values = { - 'has_USD': False + 'isUDSKeyCodeValid': False } return values[key] diff --git a/spsdk/sbfile/commands.py b/spsdk/sbfile/commands.py index e3b1340c..7f7c9eca 100644 --- a/spsdk/sbfile/commands.py +++ b/spsdk/sbfile/commands.py @@ -1,17 +1,17 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2020 NXP +# Copyright 2019-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Commands used by SBFile module.""" - +import math from abc import abstractmethod from struct import pack, unpack_from, calcsize from typing import Any, Mapping, Optional, Type -from crccheck.crc import Crc32Mpeg2 +from crcmod.predefined import mkPredefinedCrcFun from spsdk.mboot import ExtMemId from spsdk.utils.crypto.abstract import BaseClass @@ -156,6 +156,7 @@ def parse(cls, data: bytes, offset: int = 0) -> 'CmdBaseClass': class CmdNop(CmdBaseClass): """Command NOP class.""" + def __init__(self) -> None: """Initialize Command Nop.""" super().__init__(EnumCmdTag.NOP) @@ -246,7 +247,8 @@ def _update_data(self) -> None: self.data = SecBootBlckSize.align_block_fill_random(self.data) # update header self._header.count = len(self.data) - self._header.data = Crc32Mpeg2.calc(self.data, 0xFFFFFFFF) + crc32_function = mkPredefinedCrcFun('crc-32-mpeg') + self._header.data = crc32_function(self.data, 0xFFFFFFFF) @classmethod def parse(cls, data: bytes, offset: int = 0) -> 'CmdLoad': @@ -262,7 +264,8 @@ def parse(cls, data: bytes, offset: int = 0) -> 'CmdLoad': offset += CmdHeader.SIZE header_count = SecBootBlckSize.align(header.count) cmd_data = data[offset: offset + header_count] - if header.data != Crc32Mpeg2.calc(cmd_data, 0xFFFFFFFF): + crc32_function = mkPredefinedCrcFun('crc-32-mpeg') + if header.data != crc32_function(cmd_data, 0xFFFFFFFF): raise ValueError('Invalid CRC in the command header') obj = CmdLoad(header.address, cmd_data) obj.header.data = header.data @@ -295,24 +298,28 @@ def raw_size(self) -> int: size += CmdHeader.SIZE - (size % CmdHeader.SIZE) return size - def __init__(self, address: int, pattern: bytes) -> None: + def __init__(self, address: int, pattern: int, length: Optional[int] = None) -> None: """Initialize Command Fill. :param address: to write data :param pattern: data to be written + :param length: length of data to be filled, defaults to 4 :raise ValueError: raised when size is not aligned to 4 bytes """ super().__init__(EnumCmdTag.FILL) - assert isinstance(pattern, (bytes, bytearray)) - if len(pattern) < 4: - raise ValueError('pattern must be at least 4 bytes long') - if len(pattern) % 4: - raise ValueError('pattern size must be aligned to 4 bytes') + length = length or 4 + if length % 4: + raise ValueError('Length of memory range to fill must be a multiple of 4') + pattern_bytes = pattern.to_bytes(math.ceil(pattern.bit_length() / 8), 'big') + if len(pattern_bytes) not in [1, 2, 4]: + raise ValueError('Pattern must be 1, 2 or 4 bytes long') + replicate = 4 // len(pattern_bytes) + final_pattern = replicate * pattern_bytes self.address = address - self._pattern = bytes(pattern) + self._pattern = final_pattern # update header - self._header.data = unpack_from("L", self._pattern)[0] + self._header.count = length @property def pattern(self) -> bytes: @@ -327,9 +334,6 @@ def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: # export cmd data = super().export(dbg_info) # export additional data - if len(self._pattern) > 4: - data += bytes(self._pattern[4:]) - dbg_info.append_binary_data('pattern', self._pattern[4:]) data = SecBootBlckSize.align_block_fill_random(data) return data @@ -345,7 +349,7 @@ def parse(cls, data: bytes, offset: int = 0) -> 'CmdFill': assert header.tag == EnumCmdTag.FILL # The last 4 bytes of header are part of pattern value offset += CmdHeader.SIZE - 4 - return cls(header.address, data[offset: offset + header.count]) + return cls(header.address, header.data, header.count) class CmdJump(CmdBaseClass): @@ -375,19 +379,19 @@ def argument(self, value: int) -> None: @property def spreg(self) -> Optional[int]: """Return command's Stack Pointer.""" - if self._header.flags == 1: + if self._header.flags == 2: return self._header.count return None @spreg.setter - def spreg(self, value: Optional[int]) -> None: + def spreg(self, value: Optional[int] = None) -> None: """Set command's Stack Pointer.""" if value is None: self._header.flags = 0 self._header.count = 0 else: - self._header.flags = 1 + self._header.flags = 2 self._header.count = value def __init__(self, address: int = 0, argument: int = 0, spreg: Optional[int] = None) -> None: @@ -524,6 +528,7 @@ def parse(cls, data: bytes, offset: int = 0) -> 'CmdErase': class CmdReset(CmdBaseClass): """Command Reset class.""" + def __init__(self) -> None: """Initialize Command Reset.""" super().__init__(EnumCmdTag.RESET) @@ -606,6 +611,7 @@ def parse(cls, data: bytes, offset: int = 0) -> 'CmdMemEnable': class CmdProg(CmdBaseClass): """Command Program class.""" + def __init__(self) -> None: """Initialize Cmd Program.""" super().__init__(EnumCmdTag.PROG) diff --git a/spsdk/sbfile/images.py b/spsdk/sbfile/images.py index 20209ac6..12b7f4e1 100644 --- a/spsdk/sbfile/images.py +++ b/spsdk/sbfile/images.py @@ -480,6 +480,7 @@ def cert_block(self, value: CertBlockV2) -> None: """ assert isinstance(value, CertBlockV2) self._cert_block = value + self._cert_block.alignment = 16 @property def signed(self) -> bool: diff --git a/spsdk/sdp/interfaces/uart.py b/spsdk/sdp/interfaces/uart.py index d4421018..49254775 100644 --- a/spsdk/sdp/interfaces/uart.py +++ b/spsdk/sdp/interfaces/uart.py @@ -19,7 +19,7 @@ logger = logging.getLogger("SDP:UART") -def scan_uart(port: str = None, baudrate: int = 115200, timeout: int = 5000) -> List[Interface]: +def scan_uart(port: str = None, baudrate: int = None, timeout: int = None) -> List[Interface]: """Scan connected serial ports. Returns list of serial ports with devices that respond to PING command. @@ -28,10 +28,12 @@ def scan_uart(port: str = None, baudrate: int = 115200, timeout: int = 5000) -> :param port: name of preferred serial port, defaults to None :param baudrate: speed of the UART interface, defaults to 56700 - :param timeout: timeout in milliseconds + :param timeout: timeout in milliseconds, defaults to 5000 :return: list of interfaces responding to the PING command :rtype: List[spsdk.sdp.interfaces.base.Interface] """ + baudrate = baudrate or 115200 + timeout = timeout or 5000 if port: interface = _check_port(port, baudrate, timeout) return [interface] if interface else [] @@ -49,6 +51,7 @@ def _check_port(port: str, baudrate: int, timeout: int) -> Optional[Interface]: :rtype: Optional[Interface] """ try: + logger.debug(f'Checking port: {port}, baudrate: {baudrate}, timeout: {timeout}') interface = Uart(port=port, baudrate=baudrate, timeout=timeout) return interface except SerialException as e: diff --git a/spsdk/shadowregs/__init__.py b/spsdk/shadowregs/__init__.py new file mode 100644 index 00000000..841d02ff --- /dev/null +++ b/spsdk/shadowregs/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""This module contains support for Shadow Registers Tool.""" +from .shadowregs import ShadowRegisters, enable_debug diff --git a/spsdk/dat/shadow_regs.py b/spsdk/shadowregs/shadowregs.py similarity index 56% rename from spsdk/dat/shadow_regs.py rename to spsdk/shadowregs/shadowregs.py index a21f509e..1c0e6d59 100644 --- a/spsdk/dat/shadow_regs.py +++ b/spsdk/shadowregs/shadowregs.py @@ -10,9 +10,12 @@ import math from ruamel.yaml import YAML -import ruamel.yaml +from ruamel.yaml.comments import CommentedMap as CM -from spsdk.utils.registers import Registers, RegsRegister, RegConfig, BitfieldNotFound, value_to_bytes +from spsdk import __version__, __release__, __author__, SPSDK_YML_INDENT +from spsdk.utils.registers import Registers, RegsRegister +from spsdk.utils.reg_config import RegConfig +from spsdk.utils.misc import change_endianism, value_to_bytes, reverse_bytes_in_longs from spsdk.dat.dm_commands import StartDebugSession from spsdk.exceptions import SPSDKError @@ -22,47 +25,62 @@ logger = logging.getLogger(__name__) class IoVerificationError(SPSDKError): - """The error during wrie verification - exception for use with SPSDK.""" + """The error during write verification - exception for use with SPSDK.""" class ShadowRegisters(): """SPSDK support to control the shadow registers.""" def __init__(self, debug_probe: DebugProbe, config: RegConfig, device: str, revision: str = "latest") -> None: """Initialization of Shadow register class.""" - self._probe = debug_probe + self.probe = debug_probe self.config = config self.device = device self.offset = int(self.config.get_address(self.device, remove_underscore=True), 16) self.regs = Registers(self.device) - rev = revision if revision != "latest" else config.get_latest_revision(self.device) - self.regs.load_registers_from_xml(config.get_data_file(self.device, rev)) + rev = revision or "latest" + rev = rev if rev != "latest" else config.get_latest_revision(self.device) + self.regs.load_registers_from_xml( + config.get_data_file(self.device, rev), + grouped_regs=config.get_grouped_registers(self.device)) + + # Set the computed field handler + for reg, fields in self.config.get_computed_fields(self.device).items(): + reg_obj = self.regs.find_reg(reg) + reg_obj.add_setvalue_hook(self.reg_computed_fields_handler, fields) + + # Set the antipolize handler + for reg, antipole_reg in self.config.get_antipole_regs(self.device).items(): + src = self.regs.find_reg(reg) + dst = self.regs.find_reg(antipole_reg) + src.add_setvalue_hook(self.reg_antipolize_src_handler, dst) + dst.add_setvalue_hook(self.reg_antipolize_dst_handler, src) def _write_shadow_reg(self, addr: int, data: int, verify: int = True) -> None: """The function write a shadow register. - The funstion writes shadow register in to MCU and verify the write if requested. + The function writes shadow register in to MCU and verify the write if requested. param addr: Shadow register address. param data: Shadow register data to write. param verify: If True the write is read back and compare, otherwise no check is done raises IoVerificationError """ - self._probe.mem_reg_write(addr, data) + self.probe.mem_reg_write(addr, data) if verify: - readback = self._probe.mem_reg_read(addr) + readback = self.probe.mem_reg_read(addr) if readback != data: raise IoVerificationError(f"The written data 0x{data:08X} to 0x{addr:08X} address are invalid.") def reload_registers(self) -> None: """Reload all the values in managed registers.""" - for reg in self.regs.registers: + for reg in self.regs.get_registers(): self.reload_register(reg) def sets_all_registers(self) -> None: """Update all shadow registers in target by local values.""" - for reg in self.regs.registers: + for reg in self.regs.get_registers(): self.set_register(reg.name, reg.get_value()) def reload_register(self, reg: RegsRegister) -> None: @@ -72,26 +90,6 @@ def reload_register(self, reg: RegsRegister) -> None: """ reg.set_value(self.get_register(reg.name)) - @staticmethod - def _reverse_bytes_in_longs(arr: bytearray) -> bytearray: - """The function reverse byte order in longs from input bytes. - - param arr: Input array. - :return: New array with reversed bytes. - :raises ValueError: Raises when invalid value is in input. - """ - arr_len = len(arr) - if arr_len % 4 != 0: - raise ValueError("The input array is not in modulo 4!") - - result = bytearray() - - for x in range(arr_len): - word = bytearray(arr[x*4:x*4+4]) - word.reverse() - result.extend(word) - return result - def set_register(self, reg_name: str, data: Any) -> None: """The function sets the value of the specified register. @@ -99,7 +97,7 @@ def set_register(self, reg_name: str, data: Any) -> None: param data: The new data to be stored to shadow register. raises DebugProbeError: The debug probe is not specified. """ - if self._probe is None: + if self.probe is None: raise DebugProbeError("There is no debug probe.") try: @@ -115,24 +113,17 @@ def set_register(self, reg_name: str, data: Any) -> None: if width < 32: width = 32 - data_alligned = bytearray(math.ceil(width / 8)) - data_alligned[len(data_alligned) - len(value) : len(data_alligned)] = value + data_aligned = bytearray(math.ceil(width / 8)) + data_aligned[len(data_aligned) - len(value) : len(data_aligned)] = value - if reg.reverse: - data_alligned = self._reverse_bytes_in_longs(data_alligned) + end_address = start_address + math.ceil(width / 8) + addresses = range(start_address, end_address, 4) - if width == 32: - self._write_shadow_reg(start_address, int.from_bytes(data_alligned[:4], "big")) - else: - end_address = start_address + math.ceil(width / 8) - addresses = range(start_address, end_address, 4) + for i, addr in enumerate(addresses): + val = data_aligned[i*4:i*4+4] + self._write_shadow_reg(addr, int.from_bytes(change_endianism(val) if reg.reverse else val, "big")) - i = 0 - for addr in addresses: - self._write_shadow_reg(addr, int.from_bytes(data_alligned[i:i+4], "big")) - i += 4 - - reg.set_value(value) + reg.set_value(value, raw=True) except SPSDKError as exc: raise SPSDKError(f"The get shadow register failed({str(exc)}).") @@ -144,10 +135,10 @@ def get_register(self, reg_name: str) -> bytes: return: The value of requested register in bytes raises DebugProbeError: The debug probe is not specified. """ - if self._probe is None: + if self.probe is None: raise DebugProbeError("There is no debug probe.") - result = bytearray() + array = bytearray() try: reg = self.regs.find_reg(reg_name) @@ -158,16 +149,15 @@ def get_register(self, reg_name: str) -> bytes: width = 32 if width == 32: - result.extend(self._probe.mem_reg_read(start_address).to_bytes(4, "big")) + array.extend(self.probe.mem_reg_read(start_address).to_bytes(4, "big")) else: end_address = start_address + math.ceil(width / 8) addresses = range(start_address, end_address, 4) for addr in addresses: - result.extend(self._probe.mem_reg_read(addr).to_bytes(4, "big")) + array.extend(self.probe.mem_reg_read(addr).to_bytes(4, "big")) - if reg.reverse: - result = self._reverse_bytes_in_longs(result) + result = reverse_bytes_in_longs(bytes(array)) if reg.reverse else bytes(array) except SPSDKError as exc: raise SPSDKError(f"The get shadow register failed({str(exc)}).") @@ -180,41 +170,26 @@ def create_yml_config(self, file_name: str, raw: bool = False) -> None: :param file_name: The file_name (without extension) of stored configuration. :param raw: Raw output of configuration (including computed fields and anti-pole registers) """ - CM = ruamel.yaml.comments.CommentedMap # defaults to block style - - antipole_regs = self.config.get_antipole_regs(self.device) - computed_fields = self.config.get_computed_fields(self.device) + antipole_regs = None if raw else list(self.config.get_antipole_regs(self.device).values()) + computed_fields = None if raw else self.config.get_computed_fields(self.device) yaml = YAML() - yaml.indent(sequence=4, offset=2) + yaml.indent(sequence=SPSDK_YML_INDENT * 2, offset=SPSDK_YML_INDENT) data = CM() - data["registers"] = CM() - - for reg in self.regs.registers: - if not raw and reg.name in antipole_regs.values(): - continue - reg_yml = CM() - reg_yml.yaml_set_start_comment("Reg Description:" + reg.description) - reg_yml.insert(1, "name", reg.name, comment="The name of the register") - data["registers"][reg.name] = reg_yml - if len(reg.get_bitfields()) > 0: - btf_yml = CM() - reg_yml["bitfields"] = btf_yml - for i, bitf in enumerate(reg.get_bitfields()): - if not raw and reg.name in computed_fields.keys() and bitf.name in computed_fields[reg.name].keys(): - continue - possible_values = "" - if bitf.has_enums(): - # print the comments as a hint of possible values - possible_values = f", (Possible values: {', '.join(bitf.get_enum_names())})" - btf_yml.insert(i, - bitf.name, - bitf.get_enum_value(), - comment=f"The width: {bitf.width} bits{possible_values}") - else: - reg_yml.insert(2, "value", reg.get_hex_value(), comment="The value of the register") - with open(file_name, "w") as out_file: + description = CM() + description.yaml_set_start_comment(f"NXP {self.device.upper()} Shadow registers configuration", indent=2) + description.insert(1, "device", self.device, comment="The NXP device name.") + description.insert(2, "version", __version__, comment="The SPSDK Shadow register tool version.") + description.insert(3, "author", __author__, comment="The author of the configuration.") + description.insert(4, "release", __release__, comment="The SPSDK release.") + + data['description'] = description + data['registers'] = self.regs.create_yml_config( + exclude_regs=antipole_regs, + exclude_fields=computed_fields, + indent=2) + with open(file_name, "w", encoding='utf8') as out_file: yaml.dump(data, out_file) def load_yml_config(self, file_name: str, raw: bool = False) -> None: @@ -224,74 +199,71 @@ def load_yml_config(self, file_name: str, raw: bool = False) -> None: :param raw: Raw input of configuration (including computed fields and anti-pole registers) :raise SPSDKError: When the configuration file not found. """ - antipole_regs = self.config.get_antipole_regs(self.device) - computed_fields = self.config.get_computed_fields(self.device) + antipole_regs = None if raw else list(self.config.get_antipole_regs(self.device).values()) + computed_fields = None if raw else self.config.get_computed_fields(self.device) try: - with open(file_name, "r") as yml_config_file: + with open(file_name, "r", encoding='utf8') as yml_config_file: yaml = YAML() yaml.indent(sequence=4, offset=2) data = yaml.load(yml_config_file) except FileNotFoundError: raise SPSDKError("File with YML configuration doesn't exists.") - for reg in data["registers"].keys(): - if not raw and reg in antipole_regs.values(): - continue - if reg not in self.regs.get_reg_names(): - continue - #The loaded register is our - if "value" in data["registers"][reg].keys(): - val = data['registers'][reg]['value'] - val = val.replace("0x", "") - self.regs.find_reg(reg).set_value(bytes.fromhex(val)) - elif "bitfields" in data["registers"][reg].keys(): - for bitf_name in data["registers"][reg]["bitfields"]: - try: - self.regs.find_reg(reg).find_bitfield(bitf_name) - except BitfieldNotFound: - continue - if not raw and reg in computed_fields.keys() and bitf_name in computed_fields[reg].keys(): - continue - bitf = self.regs.find_reg(reg).find_bitfield(bitf_name) - if bitf.has_enums(): - #solve the bitfields store in enums string - bitf.set_enum_value(data["registers"][reg]["bitfields"][bitf_name]) - else: - #load bitfield data - bitf.set_value(int(data["registers"][reg]["bitfields"][bitf_name])) - else: - logger.error(f"There are no data for {reg} register.") - - if not raw and reg in computed_fields.keys(): - # Check the computed fields - for field in computed_fields[reg].keys(): - val = self.regs.find_reg(reg).get_value() - if hasattr(self, computed_fields[reg][field]): - method = getattr(self, computed_fields[reg][field], None) - computed_val = method(val) - self.regs.find_reg(reg).set_value(computed_val) - else: - raise SPSDKError(f"The '{computed_fields[reg][field]}' compute function doesn't exists.") - - if not raw and reg in antipole_regs.keys(): - #Write also anti-pole value - val = self.regs.find_reg(reg).get_value() - self.regs.find_reg(antipole_regs[reg]).set_value(self.antipolize_reg(val)) - - logger.debug(f"The register {reg} has been loaded from configuration.") + self.regs.load_yml_config(data['registers'], antipole_regs, computed_fields) + if not raw: + # Just update only configured registers + exclude_hooks = list(set(self.regs.get_reg_names())-set(data['registers'].keys())) + self.regs.run_hooks(exclude_hooks) - @staticmethod - def antipolize_reg(val: bytes) -> bytes: + logger.debug(f"The shadow registers has been loaded from configuration.") + + def reg_antipolize_src_handler(self, val: bytes, context: Any) -> bytes: """Antipolize given register value. :param val: Input register value. + :param context: The method context. :return: Antipolized value. """ + dst_reg: RegsRegister = context + newval = [0]*len(val) + for i, val_byte in enumerate(val): + newval[i] = val_byte ^ 0xFF + dst_reg.set_value(bytes(newval), raw=True) + + return val + + def reg_antipolize_dst_handler(self, val: bytes, context: Any) -> bytes: + """Keep same antipolized register value in computed register. + + :param val: Input register value. + :param context: The method context. + :return: Antipolized value. + """ + src_reg: RegsRegister = context + val = src_reg.get_value() newval = [0]*len(val) for i, val_byte in enumerate(val): newval[i] = val_byte ^ 0xFF return bytes(newval) + def reg_computed_fields_handler(self, val: bytes, context: Any) -> bytes: + """Recalculate all fields for given register value. + + :param val: Input register value. + :param context: The method context (fields). + :return: recomputed value. + :raises SPSDKError: Raises when the computing routine is not found. + """ + fields: dict = context + for method in fields.values(): + if hasattr(self, method): + method_ref = getattr(self, method, None) + val = method_ref(val) + else: + raise SPSDKError(f"The '{method}' compute function doesn't exists.") + + return val + # CRC8 - ITU @staticmethod def crc_update(data: bytes, crc: int = 0, is_final: bool = True) -> int: @@ -299,18 +271,18 @@ def crc_update(data: bytes, crc: int = 0, is_final: bool = True) -> int: :param data: Input data to compute CRC. :param crc: The seed for CRC. - :param is_final: The flag the the function should retrn final result. + :param is_final: The flag the the function should return final result. :return: The CRC result. """ k = 0 data_len = len(data) while data_len != 0: data_len -= 1 - c = data[k] + carry = data[k] k += 1 for i in range(8): bit = (crc & 0x80) != 0 - if (c & (0x80>>i)) != 0: + if (carry & (0x80>>i)) != 0: bit = not bit crc <<= 1 if bit: @@ -318,11 +290,10 @@ def crc_update(data: bytes, crc: int = 0, is_final: bool = True) -> int: crc &= 0xff if is_final: return (crc & 0xff) ^ 0x55 - else: - return crc & 0xff + return crc & 0xff - - def comalg_dcfg_cc_socu_crc8(self, val: bytes) -> bytes: + @staticmethod + def comalg_dcfg_cc_socu_crc8(val: bytes) -> bytes: """Function that creates the crc for DCFG_CC_SOCU. :param val: Input DCFG_CC_SOCU Value. @@ -330,12 +301,13 @@ def comalg_dcfg_cc_socu_crc8(self, val: bytes) -> bytes: """ ret = [0]*4 ret[0:3] = val[0:3] - input = bytearray(val[0:3]) - input.reverse() - ret[3] = self.crc_update(input) + in_val = bytearray(val[0:3]) + in_val.reverse() + ret[3] = ShadowRegisters.crc_update(in_val) return bytes(ret) - def comalg_dcfg_cc_socu_rsvd(self, val: bytes) -> bytes: + @staticmethod + def comalg_dcfg_cc_socu_rsvd(val: bytes) -> bytes: """Function fill up the DCFG_CC_SOCU RSVD filed by 0x40 to satisfy MCU needs. :param val: Input DCFG_CC_SOCU Value. @@ -346,7 +318,8 @@ def comalg_dcfg_cc_socu_rsvd(self, val: bytes) -> bytes: new_val[0] |= 0x40 return new_val - def comalg_do_nothig(self, val: bytes) -> bytes: + @staticmethod + def comalg_do_nothing(val: bytes) -> bytes: """Function that do nothing. :param val: Input Value. @@ -354,6 +327,7 @@ def comalg_do_nothig(self, val: bytes) -> bytes: """ return val + def enable_debug(probe: DebugProbe, ap_mem: int = 0) -> bool: """Function that enables debug access ports on devices with debug mailbox. @@ -396,8 +370,7 @@ def test_ahb_access(ap_mem: int) -> bool: logger.debug("Locked Device. Launching unlock sequence.") # Start debug mailbox system - dbg_mlbx = DebugMailbox(debug_probe=probe) - StartDebugSession(dm=dbg_mlbx).run() + StartDebugSession(dm=DebugMailbox(debug_probe=probe)).run() # Recheck the AHB access if test_ahb_access(ap_mem): diff --git a/spsdk/utils/__init__.py b/spsdk/utils/__init__.py index 190b6255..0989cbec 100644 --- a/spsdk/utils/__init__.py +++ b/spsdk/utils/__init__.py @@ -1,8 +1,22 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Module containing various functions/modules used throughout the SPSDK.""" + +import os + +from spsdk import SPSDK_DATA_FOLDER + +REGS_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, 'regs') + +from .exceptions import ( + SPSDKRegsError, + SPSDKRegsErrorRegisterGroupMishmash, + SPSDKRegsErrorRegisterNotFound, + SPSDKRegsErrorBitfieldNotFound, + SPSDKRegsErrorEnumNotFound +) diff --git a/spsdk/utils/exceptions.py b/spsdk/utils/exceptions.py new file mode 100644 index 00000000..b795a032 --- /dev/null +++ b/spsdk/utils/exceptions.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Module provides exceptions for SPSDK utilities.""" +from spsdk import SPSDKError + +class SPSDKRegsError(SPSDKError): + """General Error group for utilities SPSDK registers module.""" + +class SPSDKRegsErrorRegisterGroupMishmash(SPSDKRegsError): + """Register Group inconsistency problem.""" + +class SPSDKRegsErrorRegisterNotFound(SPSDKRegsError): + """Register has not been found.""" + +class SPSDKRegsErrorBitfieldNotFound(SPSDKRegsError): + """Bitfield has not been found.""" + +class SPSDKRegsErrorEnumNotFound(SPSDKRegsError): + """Enum has not been found.""" diff --git a/spsdk/utils/misc.py b/spsdk/utils/misc.py index 92fab7e7..a8626f5e 100644 --- a/spsdk/utils/misc.py +++ b/spsdk/utils/misc.py @@ -7,7 +7,9 @@ """Miscellaneous functions used throughout the SPSDK.""" import contextlib import os +import re from typing import Callable, Iterable, Iterator, Optional, TypeVar, List, Union +from math import ceil # for generics T = TypeVar('T') # pylint: disable=invalid-name @@ -35,8 +37,8 @@ def align_block(data: bytes, alignment: int = 4, padding: int = 0) -> bytes: assert isinstance(data, bytes) assert alignment > 0 assert -1 <= padding <= 255 - curr_size = len(data) - num_padding = align(curr_size, alignment) - curr_size + current_size = len(data) + num_padding = align(current_size, alignment) - current_size if not num_padding: return data if padding == -1: @@ -59,9 +61,9 @@ def extend_block(data: bytes, length: int, padding: int = 0) -> bytes: :param padding: 8-bit value value to be used as a padding :return: block extended with padding """ - curr_len = len(data) - assert length >= curr_len - num_padding = length - curr_len + current_len = len(data) + assert length >= current_len + num_padding = length - current_len if not num_padding: return data return data + bytes([padding]) * num_padding @@ -233,3 +235,154 @@ def lines(self) -> Iterable[str]: def info(self) -> str: """:return: multi-line text with log; empty string if nothing logged or log disabled.""" return "\n".join(self.lines) + + +def format_value(value: int, size: int, delimeter: str = '_', use_prefix: bool = True) -> str: + """Convert the 'value' into either BIN or HEX string, depending on 'size'. + + if 'size' is divisible by 8, function returns HEX, BIN otherwise + digits in result string are grouped by 4 using 'delimeter' (underscore) + """ + padding = size if size % 8 else (size // 8) * 2 + infix = 'b' if size % 8 else 'x' + parts = re.findall(".{1,4}", f"{value:0{padding}{infix}}"[::-1]) + rev = delimeter.join(parts)[::-1] + prefix = f"0{infix}" if use_prefix else "" + return f"{prefix}{rev}" + + +def get_bytes_cnt_of_int(value: int, align_to_2n: bool = True) -> int: + """Returns count of bytes needed to store handled integer. + + :param value: Input integer value. + :param align_to_2n: The result will be aligned to standard sizes 1,2,4,8,12,16,20. + :return: Number of bytes needed to store integer. + """ + cnt = 0 + if value == 0: + return 1 + + while value != 0: + value >>= 8 + cnt += 1 + + if align_to_2n and cnt > 2: + cnt = int(ceil(cnt / 4)) * 4 + + return cnt + + +def value_to_int(value: Union[bytes, bytearray, int, str], default: int = None) -> int: + """Function loads value from lot of formats to integer. + + :param value: Input value. + :param default: Default Value in case of invalid input. + :return: Value in Integer. + :raise TypeError: Unsupported input type. + """ + if isinstance(value, int): + return value + + if isinstance(value, (bytes, bytearray)): + return int.from_bytes(value, "big") + + if isinstance(value, str) and value != "": + # Remove possible underscore in HEX format + not_decimal = value.find("_") >= 0 + value = value.replace("_", "") + value = value.replace("b'", "0b") + if value.lower().startswith('0x'): + return int(value, 16) + if value.lower().startswith('0b'): + return int(value, 2) + if value.isdecimal() and not not_decimal: + return int(value) + if value[0] != '-': + try: + # try to decode hex string without '0x' prefix + return int(value, 16) + except: # pylint: disable=bare-except + pass + + if default is not None: + return default + raise TypeError(f"Invalid input number type({type(value)}) with value ({value})") + + +def value_to_bytes(value: Union[bytes, bytearray, int, str], align_to_2n: bool = True) -> bytes: + """Function loads value from lot of formats. + + :param value: Input value. + :param align_to_2n: When is set, the function aligns length of return array to 1,2,4,8,12 etc. + :return: Value in bytes. + """ + if isinstance(value, bytes): + return value + + if isinstance(value, bytearray): + return bytes(value) + + value = value_to_int(value) + return value.to_bytes(get_bytes_cnt_of_int(value, align_to_2n), "big") + + +def value_to_bool(value: Union[bool, int, str]) -> bool: + """Function decode bool value from various formats. + + :param value: Input value. + :return: Boolean value. + :raise TypeError: Unsupported input type. + """ + if isinstance(value, bool): + return value + + if isinstance(value, int): + return bool(value) + + if isinstance(value, str): + return value in ("True", "T", "1") + + raise TypeError(f"Invalid input Boolean type({type(value)}) with value ({value})") + + +def reverse_bytes_in_longs(arr: bytes) -> bytes: + """The function reverse byte order in longs from input bytes. + + :param arr: Input array. + :return: New array with reversed bytes. + :raises ValueError: Raises when invalid value is in input. + """ + arr_len = len(arr) + if arr_len % 4 != 0: + raise ValueError("The input array is not in modulo 4!") + + result = bytearray() + + for x in range(0, arr_len, 4): + word = bytearray(arr[x:x+4]) + word.reverse() + result.extend(word) + return bytes(result) + + +def change_endianism(bin_data: bytes) -> bytes: + """Convert binary format used in files to binary used in register object. + + :param bin_data: input binary array. + :return: Converted array (practically little to big endianism). + :raises ValueError: Invalid value on input. + """ + data = bytearray(bin_data) + length = len(data) + if length == 1: + return data + + if length == 2: + data.reverse() + return data + + # The length of 24 bits is not supported yet + if length == 3: + raise ValueError("Unsupported length (3) for change endianism.") + + return reverse_bytes_in_longs(data) diff --git a/spsdk/utils/reg_config.py b/spsdk/utils/reg_config.py new file mode 100644 index 00000000..cc46dc41 --- /dev/null +++ b/spsdk/utils/reg_config.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2020-2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +"""Module to handle registers configuration.""" + +import os +import logging +from typing import List, Dict +import json + +logger = logging.getLogger(__name__) + +class RegConfig(): + """Class that helps manage the registers configuration.""" + + def __init__(self, path: str) -> None: + """Register Configuration class constructor. + + :param path: The path to configuration JSON file. + """ + self.path = path + self.config = RegConfig.load_config(path) + + @classmethod + def load_config(cls, path: str) -> dict: + """Load config file. + + :param path: The path to database file. + :return: The database. + """ + with open(path) as config_file: + return json.load(config_file) + + @classmethod + def devices(cls, path: str) -> List[str]: + """Classmethod to get list of suppported devices. + + :param path: Path to database file. + :return: List of all supported devices. + """ + config = cls.load_config(path) + return list(config['devices'].keys()) + + def _get_device(self, device: str = None) -> dict: + """Return JSON device structure. + + :param device: String Key with device name. + :return: Dictionary device configuration structure or None: + """ + return self.config["devices"].get(device, None) + + def get_latest_revision(self, device: str) -> str: + """Get latest revision for device. + + :param device: The device name. + :return: The name of latest revision. + """ + return self.config["devices"][device]["latest"] + + def get_devices(self) -> List[str]: + """Get list of supported devices. + + :return: List of supported device. + """ + return list(self.config["devices"].keys()) + + def get_revisions(self, device: str) -> List[str]: + """Get list of revisions for given device. + + :param device: The device name. + :return: List of all supported device version. + """ + return list(self.config["devices"][device]["revisions"].keys()) + + def get_address(self, device: str, remove_underscore: bool = False) -> str: + """Get the area address in chip memory. + + :param device: The device name. + :param remove_underscore: Remove underscore from address if set. + :return: Base address of registers. + """ + address = self.config["devices"][device]["address"] + if remove_underscore: + return address.replace("_", "") + return address + + def get_data_file(self, device: str, revision: str) -> str: + """Return the full path to data file (xml). + + :param device: The device name. + :param revision: The chip revision. + :return: The path to data file. + """ + file_name = self.config["devices"][device]["revisions"][revision] + dir_path = os.path.dirname(os.path.abspath(self.path)) + return os.path.join(dir_path, file_name) + + def get_antipole_regs(self, device: str = None) -> Dict[str, str]: + """Return the list of inverted registers. + + :param device: The device name. + :return: The dictionary of antipole registers. + """ + dev = self._get_device(device) + if dev and "inverted_regs" in dev.keys(): + # Get device specific + inverted_regs = dev.get("inverted_regs", {}) + else: + # get the general one + inverted_regs = self.config.get("inverted_regs", {}) + return dict(inverted_regs) + + def get_computed_fields(self, device: str = None) -> Dict[str, Dict[str, str]]: + """Return the list of computed fields (not used in config YML files). + + :param device: The device name, if not specified, the general value is used. + :return: The dictionary of computed fields. + """ + dev = self._get_device(device) + if dev and "computed_fields" in dev.keys(): + # Get device specific + computed_fields = dev.get("computed_fields", {}) + else: + # get the general one + computed_fields = self.config.get("computed_fields", {}) + return dict(computed_fields) + + def get_computed_registers(self, device: str = None) -> List[str]: + """Return the list of computed registers. + + :param device: The device name, if not specified, the general value is used. + :return: The list of computed registers. + """ + dev = self._get_device(device) + if dev and "computed_registers" in dev.keys(): + # Get device specific + computed_registers = dev.get("computed_registers", []) + else: + # get the general one + computed_registers = self.config.get("computed_registers", []) + return list(computed_registers) + + def get_grouped_registers(self, device: str = None) -> List[dict]: + """Return the list of grouped registers description. + + :param device: The device name, if not specified, the general value is used. + :return: The list of grouped registers descriptions. + """ + dev = self._get_device(device) + if dev and "grouped_registers" in dev.keys(): + # Get device specific + grouped_registers = dev.get("grouped_registers", []) + else: + # get the general one + grouped_registers = self.config.get("grouped_registers", []) + return list(grouped_registers) + + def get_ignored_registers(self, device: str = None) -> List[str]: + """Return the list of ignored registers. + + :param device: The device name, if not specified, the general value is used. + :return: The list of ignored register. + """ + dev = self._get_device(device) + if dev and "ignored_registers" in dev.keys(): + # Get device specific + ignored_registers = dev.get("ignored_registers", []) + else: + # get the general one + ignored_registers = self.config.get("ignored_registers", []) + return list(ignored_registers) + + def get_ignored_fields(self, device: str = None) -> List[str]: + """Return the list of ignored fields. + + :param device: The device name, if not specified, the general value is used. + :return: The list of ignored fields. + """ + dev = self._get_device(device) + if dev and "ignored_fields" in dev.keys(): + # Get device specific + ignored_fields = dev.get("ignored_fields", []) + else: + # get the general one + ignored_fields = self.config.get("ignored_fields", []) + return list(ignored_fields) + + def get_seal_start_address(self, device: str = None) -> str: + """Return the seal start address. + + :param device: The device name, if not specified, the general value is used. + :return: The seal start register name. + """ + dev = self._get_device(device) + if dev and "seal_start" in dev.keys(): + # Get device specific + seal_start = dev.get("seal_start", None) + else: + # get the general one + seal_start = self.config.get("seal_start", None) + return seal_start + + def get_seal_count(self, device: str = None) -> str: + """Return the seal count. + + :param device: The device name, if not specified, the general value is used. + :return: The seal count. + """ + dev = self._get_device(device) + if dev and "seal_count" in dev.keys(): + # Get device specific + seal_count = dev.get("seal_count", None) + else: + # get the general one + seal_count = self.config.get("seal_count", None) + return seal_count diff --git a/spsdk/utils/registers.py b/spsdk/utils/registers.py index 81ab526f..1cc16e31 100644 --- a/spsdk/utils/registers.py +++ b/spsdk/utils/registers.py @@ -6,72 +6,27 @@ # SPDX-License-Identifier: BSD-3-Clause """Module to handle registers descriptions with support for XML files.""" -import os import logging -from math import log, ceil -from typing import List, Dict, Any -import json - +from math import ceil +from typing import Callable, List, Dict, Any, Mapping, Union import xml.etree.ElementTree as ET from xml.dom import minidom +from ruamel.yaml.comments import CommentedMap as CM +from jinja2 import Environment, FileSystemLoader -from spsdk.exceptions import SPSDKError - -logger = logging.getLogger(__name__) - -class RegisterNotFound(SPSDKError): - """Register has not found.""" - pass - -class BitfieldNotFound(SPSDKError): - """Bitfield has not found.""" - pass - -class EnumNotFound(SPSDKError): - """Enum has not found.""" - pass - -def value_to_bytes(value: Any, align_to_2n: bool = True) -> bytes: - """Function loads value from lot of formats. - - :param value: Input value. - :param align_to_2n: When is set, the function aligns legth of return array to 1,2,4,8,12 etc. - :return: Value in bytes. - :raise TypeError: Unsupported input type. - """ - def bytes_cnt(value: int, align_to_2n: bool = True) -> int: - val = 1 if value == 0 else int(log(value, 256)) + 1 - if align_to_2n and val > 2: - val = int(ceil(val / 4)) * 4 - - return val - - if isinstance(value, bytes): - return value - - if isinstance(value, bytearray): - return bytes(value) +from spsdk import SPSDK_YML_INDENT +from spsdk import utils +from spsdk.utils.misc import format_value, value_to_bool, value_to_int, value_to_bytes +from spsdk.utils.exceptions import (SPSDKRegsError, + SPSDKRegsErrorRegisterNotFound, + SPSDKRegsErrorBitfieldNotFound, + SPSDKRegsErrorEnumNotFound, + SPSDKRegsErrorRegisterGroupMishmash) - if isinstance(value, int): - return value.to_bytes(bytes_cnt(value, align_to_2n), "big") - - if isinstance(value, str): - if value.lower().find('0x') >= 0: - val = int(value, 16) - return val.to_bytes(bytes_cnt(val, align_to_2n), "big") - if value.lower().find('0b') >= 0: - val = int(value, 2) - return val.to_bytes(bytes_cnt(val, align_to_2n), "big") - if value.lower().find("b'") >= 0: - value = value.replace("b'", "0b") - val = int(value, 2) - return val.to_bytes(bytes_cnt(val, align_to_2n), "big") - if value.isdecimal(): - val = int(value) - return val.to_bytes(bytes_cnt(val, align_to_2n), "big") - - raise TypeError(f"Invalid input number type({type(value)})") +HTMLDataElement = Mapping[str, Union[str, dict, list]] +HTMLData = List[HTMLDataElement] +logger = logging.getLogger(__name__) class RegsEnum(): """Storage for register enumerations.""" @@ -115,7 +70,7 @@ def get_value_int(self) -> int: """ return int.from_bytes(self.value, "big") - def _get_value_str(self) -> str: + def get_value_str(self) -> str: """Method returns formated value. :return: Formatted string with enum value. @@ -124,10 +79,7 @@ def _get_value_str(self) -> str: return "N/A" val = self.get_value_int() - if self.max_width == 0: - return bin(val) - - return f"0b{val:0{self.max_width}b}" + return format_value(val, self.max_width) def add_et_subelement(self, parent: ET.Element) -> None: """Creates the register XML structure in ElementTree. @@ -136,18 +88,18 @@ def add_et_subelement(self, parent: ET.Element) -> None: """ element = ET.SubElement(parent, "bit_field_value") element.set("name", self.name) - element.set("value", self._get_value_str()) + element.set("value", self.get_value_str()) element.set("description", self.description) def __str__(self) -> str: - """Overrided 'ToString()' to print register. + """Overrides 'ToString()' to print register. :return: Friendly string with enum information. """ output = "" output += f"Name: {self.name}\n" - output += f"Value: {self._get_value_str()}\n" + output += f"Value: {self.get_value_str()}\n" output += f"Description: {self.description}\n" return output @@ -160,7 +112,7 @@ def __init__(self, offset: int, width: int, description: str = None, - reset_val: str = "N/A", + reset_val: Any = "0", access: str = "RW") -> None: """Constructor of RegsBitField class. Used to store bitfield information. @@ -177,9 +129,12 @@ def __init__(self, self.offset = offset self.width = width self.description = description or "N/A" - self.reset_value = reset_val + self.reset_value = value_to_int(reset_val, 0) self.access = access self._enums: List[RegsEnum] = [] + self._update_reset_value() + self.set_value(self.reset_value, raw=True) + @classmethod def from_xml_element(cls, xml_element: ET.Element, parent: 'RegsRegister') -> 'RegsBitField': @@ -190,12 +145,12 @@ def from_xml_element(cls, xml_element: ET.Element, parent: 'RegsRegister') -> 'R :return: The instance of this class. """ name = xml_element.attrib["name"] if "name" in xml_element.attrib else "N/A" - offset = int(xml_element.attrib["offset"], 16) if "offset" in xml_element.attrib else 0 - width = int(xml_element.attrib["width"]) if "width" in xml_element.attrib else 0 + offset = value_to_int(xml_element.attrib["offset"]) if "offset" in xml_element.attrib else 0 + width = value_to_int(xml_element.attrib["width"]) if "width" in xml_element.attrib else 0 descr = xml_element.attrib["description"] if "description" in xml_element.attrib else "N/A" - access = xml_element.attrib["access"] if "access" in xml_element.attrib else "N/A" - reset_value = xml_element.attrib["reset_value"] \ - if "reset_value" in xml_element.attrib else "N/A" + access = xml_element.attrib["access"] if "access" in xml_element.attrib else "R/W" + reset_value = value_to_int(xml_element.attrib["reset_value"]) \ + if "reset_value" in xml_element.attrib else 0 bitfield = cls(parent, name, offset, @@ -211,7 +166,7 @@ def from_xml_element(cls, xml_element: ET.Element, parent: 'RegsRegister') -> 'R return bitfield def has_enums(self) -> bool: - """Returns if the bitfileds has enums. + """Returns if the bitfields has enums. :return: True is has enums, False otherwise. """ @@ -242,30 +197,50 @@ def get_value(self) -> int: value = value & mask return value - def set_value(self, new_val: int) -> None: + def get_reset_value(self) -> int: + """Returns integer reset value of the bitfield. + + :return: Reset value of bitfield. + """ + return self.reset_value + + def set_value(self, new_val: Any, raw: bool = False) -> None: """Updates the value of the bitfield. :param new_val: New value of bitfield. + :param raw: If set, no automatic modification of value is applied. :raise ValueError: The input value is out of range. """ - if new_val > 1< 1< None: + """Updates the reset value of the bitfield in register.""" + reg_val = int.from_bytes(self.parent.get_value(), "big") + mask = ((1< None: + def set_enum_value(self, new_val: str, raw: bool = False) -> None: """Updates the value of the bitfield by its enum value. :param new_val: New enum value of bitfield. + :param raw: If set, no automatic modification of value is applied. """ - self.set_value(self.get_enum_constant(new_val)) + self.set_value(self.get_enum_constant(new_val), raw) - def get_enum_value(self) -> Any: + def get_enum_value(self) -> Union[str, int]: """Returns enum value of the bitfield. :return: Current value of bitfield. @@ -275,19 +250,19 @@ def get_enum_value(self) -> Any: for enum in self._enums: if enum.get_value_int() == value: return enum.name - return int(value) + return value def get_enum_constant(self, enum_name: str) -> int: """Returns constant representation of enum by its name. :return: Constant of enum. - :raises EnumNotFound: The enum has not been found. + :raises SPSDKRegsErrorEnumNotFound: The enum has not been found. """ for enum in self._enums: if enum.name == enum_name: return enum.get_value_int() - raise EnumNotFound("The enum for {enum_name} has not been found.") + raise SPSDKRegsErrorEnumNotFound("The enum for {enum_name} has not been found.") def get_enum_names(self) -> List[str]: """Returns list of the enum strings. @@ -306,13 +281,30 @@ def add_et_subelement(self, parent: ET.Element) -> None: element.set("width", str(self.width)) element.set("name", self.name) element.set("access", self.access) - element.set("reset_value", str(self.reset_value)) + element.set("reset_value", format_value(self.reset_value, self.width)) element.set("description", self.description) for enum in self._enums: enum.add_et_subelement(element) + def get_html_data_element(self) -> HTMLDataElement: + """Returns HTML element of bitfield. + + :return: HTML element. + """ + enum_desc = {} + for enum in self.get_enums(): + enum_desc[enum.get_value_str()] = enum.description + + return { + 'name': self.name, + 'desc': self.description, + 'width': str(self.width), + 'offset': str(self.offset), + 'bit_values': enum_desc + } + def __str__(self) -> str: - """Overrided 'ToString()' to print register. + """Override 'ToString()' to print register. :return: Friendly looking string that describes the bitfield. """ @@ -353,11 +345,16 @@ def __init__(self, self.offset = offset self.width = width self.description = description or "N/A" - self.access = access or "N/A" + self.access = access or "RW" self.reverse = reverse self._bitfields: List[RegsBitField] = [] - self.value = bytes([0]) - + self._set_value_hooks: list = [] + self._value = bytes(width//8) + self._reset_value = bytes(width//8) + # Grouped register members + self.sub_regs: List[RegsRegister] = [] + self._sub_regs_width_init = False + self._sub_regs_width = 0 @classmethod def from_xml_element(cls, xml_element: ET.Element) -> 'RegsRegister': """Initialization register by XML ET element. @@ -366,11 +363,11 @@ def from_xml_element(cls, xml_element: ET.Element) -> 'RegsRegister': :return: The instance of this class. """ name = xml_element.attrib["name"] if "name" in xml_element.attrib else "N/A" - offset = int(xml_element.attrib["offset"], 16) if "offset" in xml_element.attrib else 0 - width = int(xml_element.attrib["width"]) if "width" in xml_element.attrib else 0 + offset = value_to_int(xml_element.attrib["offset"]) if "offset" in xml_element.attrib else 0 + width = value_to_int(xml_element.attrib["width"]) if "width" in xml_element.attrib else 0 descr = xml_element.attrib["description"] if "description" in xml_element.attrib else "N/A" reverse = (xml_element.attrib["reversed"] if "reversed" in xml_element.attrib else "False") == "True" - access = "RW" #TODO solve this + access = xml_element.attrib["access"] if "access" in xml_element.attrib else "RW" reg = cls(name, offset, width, descr, reverse, access) if xml_element.text: @@ -379,6 +376,57 @@ def from_xml_element(cls, xml_element: ET.Element) -> 'RegsRegister': reg.add_bitfield(RegsBitField.from_xml_element(xml_bitfield, reg)) return reg + def has_group_registers(self) -> bool: + """Returns true if register is compounded from sub-registers. + + :return: True if register has sub-registers, False otherwise. + """ + return len(self.sub_regs) > 0 + + def add_group_reg(self, reg: "RegsRegister") -> None: + """Add group element for this register. + + :param reg: Register member of this register group. + :raises SPSDKRegsErrorRegisterGroupMishmash: When any inconsistency is detected. + """ + first_member = not self.has_group_registers() + if first_member: + if self.offset == 0: + self.offset = reg.offset + if self.width == 0: + self.width = reg.width + else: + self._sub_regs_width_init = True + self._sub_regs_width = reg.width + if reg.reverse: + self.reverse = True + if self.access == "RW": + self.access = reg.access + else: + # There is strong rule that supported group MUST be in one row in memory! + if not self._sub_regs_width_init: + if self.offset + self.width // 8 != reg.offset: + raise SPSDKRegsErrorRegisterGroupMishmash( + f"The register {reg.name} doesn't follow the previous one.") + self.width += reg.width + else: + if self.offset + self.width // 8 <= reg.offset: + raise SPSDKRegsErrorRegisterGroupMishmash( + f"The register {reg.name} doesn't follow the previous one.") + self._sub_regs_width += reg.width + if self._sub_regs_width > self.width: + raise SPSDKRegsErrorRegisterGroupMishmash( + f"The register {reg.name} bigger width than is defined.") + if self.sub_regs[0].width != reg.width: + raise SPSDKRegsErrorRegisterGroupMishmash(f"The register {reg.name} has different width.") + if self.access != reg.access: + raise SPSDKRegsErrorRegisterGroupMishmash(f"The register {reg.name} has different access type.") + + if self.reverse: + reg.reverse = True + + self.sub_regs.append(reg) + def add_et_subelement(self, parent: ET.Element) -> None: """Creates the register XML structure in ElementTree. @@ -393,21 +441,92 @@ def add_et_subelement(self, parent: ET.Element) -> None: for bitfield in self._bitfields: bitfield.add_et_subelement(element) - def set_value(self, val: Any) -> None: - """Set the new value of register.""" + def get_html_data_element(self, exclude: List[str] = None) -> HTMLDataElement: + """Returns HTML element of register. + + :return: HTML element. + """ + bitfield_desc = [] + for bitfield in self.get_bitfields(exclude): + bitfield_desc.append(bitfield.get_html_data_element()) + + return { + 'name': self.name, + 'desc': self.description, + 'width': str(self.width), + 'offset': hex(self.offset), + 'bitfields': bitfield_desc + } + + def set_value(self, val: Any, raw: bool = False) -> None: + """Set the new value of register. + + :param val: The new value to set. + :param raw: Do not use any modification hooks. + """ try: - self.value = value_to_bytes(val) + value = value_to_bytes(val) + #TODO check if this works with shorter arrays for example with ROTKH + value = value.rjust(self.width // 8, b'\x00') + if not raw and len(self._set_value_hooks) > 0: + for hook in self._set_value_hooks: + value = hook[0](value, hook[1]) + + self._value = value + + if self.has_group_registers(): + # Update also values in sub registers + for index, sub_reg in enumerate(self.sub_regs): + sub_reg.set_value(value[index*4:index*4+4]) + except TypeError: - logger.error("Loaded invalid value {str(val)}") - self.value = b'' + logger.error(f"Loaded invalid value {str(val)}") + + def reset_value(self, raw: bool = False) -> None: + """Reset the value of register. + + :param raw: Do not use any modification hooks. + """ + value = bytearray(int(self.width//8)) + int_val = 0 + for bitfield in self.get_bitfields(): + width = bitfield.width + offset = bitfield.offset + val = bitfield.reset_value + int_val |= (val & ((1< bytes: """Get the value of register.""" - return self.value + if self.has_group_registers(): + # Update local value, by the sub register values + sub_regs_value = bytearray(self.width // 8) + for index, sub_reg in enumerate(self.sub_regs): + sub_regs_value[index*4:index*4+4] = sub_reg.get_value() + if sub_regs_value != self._value: + self.set_value(sub_regs_value, raw=True) + + return self._value + + def get_int_value(self) -> int: + """Get the integer value of register.""" + return int.from_bytes(self.get_value(), "big") def get_hex_value(self) -> str: """Get the value of register in string hex format.""" - return "0x"+ self.value.hex().replace("'", "") + return format_value( + self.get_int_value(), + self.width, + delimeter="", + use_prefix=self.width <= 32) + + def get_reset_value(self) -> bytes: + """Returns reset value of the register. + + :return: Reset value of register. + """ + return self._reset_value def add_bitfield(self, bitfield: RegsBitField) -> None: """Add register bitfield. @@ -416,28 +535,48 @@ def add_bitfield(self, bitfield: RegsBitField) -> None: """ self._bitfields.append(bitfield) - def get_bitfields(self) -> List[RegsBitField]: + def get_bitfields(self, exclude: List[str] = None) -> List[RegsBitField]: """Returns register bitfields. - :return: Returns List of added register bitfields. + Method allows exclude some bitfields by their names. + :param exclude: Exclude list of bitfield names if needed. + :return: Returns List of register bitfields. """ + if exclude: + return [r for r in self._bitfields if not r.name.startswith(tuple(exclude))] return self._bitfields + def get_bitfield_names(self, exclude: List[str] = None) -> List[str]: + """Returns list of the bitfield names. + + :param exclude: Exclude list of bitfield names if needed. + :return: List of bitfield names. + """ + return [x.name for x in self.get_bitfields(exclude)] + def find_bitfield(self, name: str) -> RegsBitField: """Returns the instance of the bitfield by its name. :param name: The name of the bitfield. :return: The bitfield instance. - :raises BitfieldNotFound: The register doesn't exists. + :raises SPSDKRegsErrorBitfieldNotFound: The register doesn't exists. """ - for bitf in self._bitfields: - if name == bitf.name: - return bitf + for bitfield in self._bitfields: + if name == bitfield.name: + return bitfield + + raise SPSDKRegsErrorBitfieldNotFound(f" The {name} is not found in register {self.name}.") - raise BitfieldNotFound(f" The {name} is not found in register {self.name}.") + def add_setvalue_hook(self, hook: Callable, context: Any = None) -> None: + """Set the value hook for write operation. + + :param hook: Callable hook for set value operation. + :param context: Context data for this hook. + """ + self._set_value_hooks.append((hook, context)) def __str__(self) -> str: - """Overrided 'ToString()' to print register. + """Override 'ToString()' to print register. :return: Friendly looking string that describes the register. """ @@ -462,22 +601,27 @@ def __str__(self) -> str: class Registers(): """SPSDK Class for registers handling.""" def __init__(self, device_name: str) -> None: - """Initialization of Registr class.""" - self.registers: List[RegsRegister] = [] + """Initialization of Registers class.""" + self._registers: List[RegsRegister] = [] self.dev_name = device_name - def find_reg(self, name: str) -> RegsRegister: + def find_reg(self, name: str, include_group_regs: bool = False) -> RegsRegister: """Returns the instance of the register by its name. :param name: The name of the register. + :param include_group_regs: The algorithm will check also group registers. :return: The register instance. - :raises RegisterNotFound: The register doesn't exists. + :raises SPSDKRegsErrorRegisterNotFound: The register doesn't exists. """ - for reg in self.registers: + for reg in self._registers: if name == reg.name: return reg + elif include_group_regs and reg.has_group_registers(): + for sub_reg in reg.sub_regs: + if name == sub_reg.name: + return sub_reg - raise RegisterNotFound(f" The {name} is not found in loaded registers for {self.dev_name} device.") + raise SPSDKRegsErrorRegisterNotFound(f"The {name} is not found in loaded registers for {self.dev_name} device.") def add_register(self, reg: RegsRegister) -> None: """Adds register into register list. @@ -489,7 +633,7 @@ def add_register(self, reg: RegsRegister) -> None: raise TypeError("The 'reg' has invalid type.") if reg.name not in self.get_reg_names(): - self.registers.append(reg) + self._registers.append(reg) else: logger.warning(f"Cannot add register with same name: {reg.name}.") @@ -502,36 +646,75 @@ def remove_register(self, reg: RegsRegister) -> None: if not isinstance(reg, RegsRegister): raise TypeError("The 'reg' has invalid type.") - self.registers.remove(reg) + self._registers.remove(reg) def remove_register_by_name(self, reg_names: List[str]) -> None: """Removes register from register list by List of its names. :reg_names: List of names of registers that should be removed. """ - for reg in self.registers: + for reg in self._registers: if any(reg.name in name for name in reg_names): - self.registers.remove(reg) + self._registers.remove(reg) + + def get_registers(self, exclude: List[str] = None, include_group_regs: bool = False) -> List[RegsRegister]: + """Returns list of the registers. - def get_reg_names(self) -> List[str]: + Method allows exclude some register by their names. + :param exclude: Exclude list of register names if needed. + :param include_group_regs: The algorithm will check also group registers. + :return: List of register names. + """ + if exclude: + regs = [r for r in self._registers if not r.name.startswith(tuple(exclude))] + else: + regs = self._registers.copy() + if include_group_regs: + sub_regs = [] + for reg in regs: + if reg.has_group_registers(): + sub_regs.extend(reg.sub_regs) + regs.extend(sub_regs) + + return regs + + def get_reg_names(self, exclude: List[str] = None, include_group_regs: bool = False) -> List[str]: """Returns list of the register names. + :param exclude: Exclude list of register names if needed. + :param include_group_regs: The algorithm will check also group registers. :return: List of register names. """ - return [x.name for x in self.registers] + return [x.name for x in self.get_registers(exclude, include_group_regs)] def clear(self) -> None: """Method clears the regs class.""" - self.registers.clear() + self._registers.clear() + + def run_hooks(self, exclude: List[str] = None) -> None: + """The method run hooks on all regular registers. + + :param exclude: The list of register names to be excluded. + """ + for reg in self.get_registers(exclude): + reg.set_value(reg.get_value(), False) + + def reset_values(self, exclude: List[str] = None) -> None: + """The method reset values in registers. + + :param exclude: The list of register names to be excluded. + """ + for reg in self.get_registers(exclude): + reg.reset_value(True) def __str__(self) -> str: - """Overrided 'ToString()' to print register. + """Override 'ToString()' to print register. :return: Friendly looking string that describes the registers. """ output = "" output += "Device name: " + self.dev_name + "\n" - for reg in self.registers: + for reg in self._registers: output += str(reg) + "\n" return output @@ -542,13 +725,35 @@ def write_xml(self, file_name: str) -> None: :param file_name: The name of XML file that should be created. """ xml_root = ET.Element("regs") - for reg in self.registers: + for reg in self._registers: reg.add_et_subelement(xml_root) with open(file_name, 'w', encoding="utf-8") as xml_file: no_pretty_data = minidom.parseString(ET.tostring(xml_root, encoding="unicode", short_empty_elements=False)) xml_file.write(no_pretty_data.toprettyxml()) + def generate_html( + self, heading1: str, + heading2: str, + regs_exclude: List[str] = None, + fields_exclude: List[str] = None, + ) -> str: + """Generate describing HTML file with registers. + + :param heading1: The main title in HTML. + :param heading2: The sub-title in HTML. + :param regs_exclude: The exclude registers list. + :param fields_exclude: The exclude bitfield list. + :return: The content of HTML file. + """ + data: HTMLData = [] + for reg in self.get_registers(regs_exclude): + data.append(reg.get_html_data_element(fields_exclude)) + + jinja_env = Environment(loader=FileSystemLoader(utils.REGS_DATA_FOLDER)) + template = jinja_env.get_template("regs_desc_template.html") + return template.render(heading1=heading1, heading2=heading2, data=data) + # pylint: disable=no-self-use #It's better to have this function visually close to callies def _filter_by_names(self, items: List[ET.Element], names: List[str]) -> List[ET.Element]: """Filter out all items in the "items" tree,whose name starts with one of the strings in "names" list. @@ -559,74 +764,184 @@ def _filter_by_names(self, items: List[ET.Element], names: List[str]) -> List[ET """ return [item for item in items if not item.attrib["name"].startswith(tuple(names))] -# pylint: disable=dangerous-default-value - def load_registers_from_xml(self, xml: str, filter_reg: List[str] = None) -> None: + # pylint: disable=dangerous-default-value + def load_registers_from_xml(self, xml: str, + filter_reg: List[str] = None, + grouped_regs: List[dict] = None) -> None: """Function loads the registers from the given XML. :param xml: Input XML data in string format. :param filter_reg: List of register names that should be filtered out. + :param grouped_regs: List of register prefixes names to be grouped int one. + :raises SPSDKRegsError: XML parse problem occuress. """ - xml_elements = ET.parse(xml) + def is_reg_in_group(reg: str) -> Union[dict, None]: + """Help function to recognize if the register should be part of group.""" + if grouped_regs: + for group in grouped_regs: + if reg.startswith(group["name"]): + return group + return None + + try: + xml_elements = ET.parse(xml) + except ET.ParseError as exc: + raise SPSDKRegsError(f"Cannot Parse XML data: {str(exc)}") xml_registers = xml_elements.findall("register") xml_registers = self._filter_by_names(xml_registers, filter_reg or []) # Load all registers into the class for xml_reg in xml_registers: - self.add_register(RegsRegister.from_xml_element(xml_reg)) - -class RegConfig(): - """Class that helps manage the registers configuration.""" - - def __init__(self, path: str): - """Register Configuration class consructor. - - :param path: The path to configuration JSON file. - """ - self.path = path - self.config = RegConfig.load_config(path) - - @classmethod - def load_config(cls, path: str) -> dict: - """Load config file.""" - with open(path) as config_file: - return json.load(config_file) - - @classmethod - def devices(cls, path: str) -> List[str]: - """Classmethod to get list of supppoted devices.""" - config = cls.load_config(path) - return list(config['devices'].keys()) - - def get_latest_revision(self, device: str) -> str: - """Get latest revision for device.""" - return self.config["devices"][device]["latest"] - - def get_devices(self) -> List[str]: - """Get list of supported devices.""" - return list(self.config["devices"].keys()) - - def get_revisions(self, device: str) -> List[str]: - """Get list of revisions for given device.""" - return list(self.config["devices"][device]["revisions"].keys()) - - def get_address(self, device: str, remove_underscore: bool = False) -> str: - """Get the area address in chip memory.""" - address = self.config["devices"][device]["address"] - if remove_underscore: - return address.replace("_", "") - return address - - def get_data_file(self, device: str, revision: str) -> str: - """Return the full path to data file (xml).""" - file_name = self.config["devices"][device]["revisions"][revision] - dir_path = os.path.dirname(os.path.abspath(self.path)) - return os.path.join(dir_path, file_name) - - def get_antipole_regs(self, device: str) -> Dict[str, str]: - """Return the list of inverted registers.""" - inverted_regs = self.config["devices"][device]["inverted_regs"] - return inverted_regs - - def get_computed_fields(self, device: str) -> Dict[str, Dict[str, str]]: - """Return the list of computed fileds (not used in config YML files).""" - inverted_regs = self.config["devices"][device]["computed_fields"] - return inverted_regs + group = is_reg_in_group(xml_reg.attrib["name"]) + if group: + try: + group_reg = self.find_reg(group["name"]) + except SPSDKRegsErrorRegisterNotFound: + group_reg = RegsRegister( + name=group["name"], + offset=value_to_int(group.get("offset", 0)), + width=value_to_int(group.get("width", 0)), + description=group.get("description", f"Group of {group['name']} registers."), + reverse=value_to_bool(group.get("reverse", False)), + access=group.get("access", None) + ) + self.add_register(group_reg) + group_reg.add_group_reg(RegsRegister.from_xml_element(xml_reg)) + else: + self.add_register(RegsRegister.from_xml_element(xml_reg)) + + def load_yml_config(self, + yml_data: Any, + exclude_regs: List[str] = None, + exclude_fields: Dict[str, Dict[str, str]] = None) -> None: + """The function loads the configuration from YML file. + + :param yml_data: The YAML commented data with register values. + :param exclude_regs: List of excluded registers + :param exclude_fields: Dictionary with lists of excluded bitfields + """ + for reg_name in yml_data.keys(): + if reg_name not in self.get_reg_names(exclude_regs, include_group_regs=True): + continue + #The loaded register is our + register = self.find_reg(reg_name, include_group_regs=True) + if "value" in yml_data[reg_name].keys(): + val = value_to_bytes(yml_data[reg_name]['value']) + register.set_value(val, True) + elif "bitfields" in yml_data[reg_name].keys(): + for bitfield_name in yml_data[reg_name]["bitfields"]: + try: + bitfield = register.find_bitfield(bitfield_name) + except SPSDKRegsErrorBitfieldNotFound: + continue + if exclude_fields and \ + reg_name in exclude_fields.keys() and \ + bitfield_name in exclude_fields[reg_name]: + continue + + if bitfield.has_enums(): + #solve the bitfields store in enums string + bitfield.set_enum_value(yml_data[reg_name]["bitfields"][bitfield_name], True) + else: + #load bitfield data + bitfield.set_value(value_to_int(yml_data[reg_name]["bitfields"][bitfield_name]), True) + else: + logger.error(f"There are no data for {reg_name} register.") + + logger.debug(f"The register {reg_name} has been loaded from configuration.") + + def _get_bitfield_yaml_description(self, bitfield: RegsBitField, indent: int) -> str: + """Create the valuable comment for bitfield. + + :param bitfield: Bitfield used to generate description. + :param indent: Indent for multiline comment. + :return: Bitfield YAML description. + """ + def new_line(comment: str) -> str: + return f"\n{' '*indent}# {comment}" + + description = f"Width: {bitfield.width}b[0-{(1< Any: + """The function creates the configuration YML file. + + :param white_list: Dictionary with lists of registers and its bitfields + :param diff: Get only configuration with difference value to reset state. + :param indent: Indent in space to generate YML. + :return: YAML commented map of registers value. + """ + data = CM() + + for reg in self.get_registers(): + if white_list and reg.name not in white_list.keys(): + continue + reg_yml = CM() + if diff and reg.get_value() == reg.get_reset_value(): + continue + if reg.description not in ("", "."): + reg_yml.yaml_set_start_comment( + "Reg Description: " + reg.description.replace(" ", "\n# "), + indent=indent) + data[reg.name] = reg_yml + bitfields = reg.get_bitfields() + if len(bitfields) > 0 and white_list and white_list[reg.name]: + btf_yml = CM() + for i, bitfield in enumerate(bitfields): + if diff and bitfield.get_value() == bitfield.get_reset_value(): + continue + if white_list and \ + isinstance(white_list[reg.name], list) and \ + bitfield.name not in white_list[reg.name]: + continue + btf_yml.insert( + pos=i, + key=bitfield.name, + value=bitfield.get_enum_value(), + comment=self._get_bitfield_yaml_description(bitfield, + indent + SPSDK_YML_INDENT + SPSDK_YML_INDENT)) + reg_yml.insert(1, "bitfields", btf_yml, comment="The register bitfields") + else: + reg_yml.insert(1, "value", reg.get_hex_value(), comment=f"The value width: {reg.width}b") + + return data + + def create_yml_config(self, + exclude_regs: List[str] = None, + exclude_fields: Dict[str, Dict[str, str]] = None, + ignored_fields: List[str] = None, + diff: bool = False, + indent: int = 0) -> Any: + """The function creates the configuration YML file. + + :param exclude_regs: List of excluded registers + :param exclude_fields: Dictionary with lists of excluded bitfields per register + :param ignored_fields: List of ignored names in fields. + :param diff: Get only configuration with difference value to reset state. + :param indent: Indent in space to generate YML. + :return: YAML commented map of registers value. + """ + white_list = {} + for reg in self._registers: + if exclude_regs and reg.name.startswith(tuple(exclude_regs)): + continue + value = [] + for bitfield in reg.get_bitfields(): + if ignored_fields and bitfield.name.startswith(tuple(ignored_fields)): + continue + if exclude_fields and reg.name in exclude_fields.keys() and exclude_fields[reg.name]: + if bitfield.name.startswith(tuple(exclude_fields[reg.name])): + continue + value.append(bitfield.name) + white_list[reg.name] = value if len(value) > 0 else None + + return self.create_yml_config_white_list(white_list if white_list else None, diff, indent) diff --git a/tests/dat/test_nxpdebugmbox.py b/tests/dat/test_nxpdebugmbox.py index 26a33238..c9bd5dcd 100644 --- a/tests/dat/test_nxpdebugmbox.py +++ b/tests/dat/test_nxpdebugmbox.py @@ -4,7 +4,7 @@ # Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -""" Tests for nxpkeygen utility.""" +""" Tests for nxpdebugmbox utility.""" from click.testing import CliRunner diff --git a/tests/debuggers/conftest.py b/tests/debuggers/conftest.py index 45b8d07c..8e12eaa6 100644 --- a/tests/debuggers/conftest.py +++ b/tests/debuggers/conftest.py @@ -4,7 +4,7 @@ # Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause - +"""Test module for debug probes used by SPSDK.""" from spsdk.debuggers.utils import PROBES from tests.debuggers.debug_probe_virtual import DebugProbeVirtual diff --git a/tests/debuggers/debug_probe_virtual.py b/tests/debuggers/debug_probe_virtual.py index a57fa441..f3c05a99 100644 --- a/tests/debuggers/debug_probe_virtual.py +++ b/tests/debuggers/debug_probe_virtual.py @@ -11,11 +11,13 @@ import json from typing import Dict, Any -from spsdk.debuggers.debug_probe import (DebugProbe, - DebugProbeTransferError, - DebugProbeNotOpenError, - DebugProbeError, - DebugProbeMemoryInterfaceNotEnabled) +from spsdk.debuggers.debug_probe import ( + DebugProbe, + DebugProbeTransferError, + DebugProbeNotOpenError, + DebugProbeError, + DebugProbeMemoryInterfaceNotEnabled +) logger = logging.getLogger(__name__) @@ -76,7 +78,7 @@ def get_connected_probes(cls, hardware_id: str = None, user_params: Dict = None) :return: probe_description :raises DebugProbeError: In case of invoked test Exception. """ - #TODO fix problems with cyclic import + #pylint: disable=import-outside-toplevel from spsdk.debuggers.utils import DebugProbes, ProbeDescription probes = DebugProbes() @@ -210,7 +212,7 @@ def coresight_reg_read(self, access_port: bool = True, addr: int = 0) -> int: """Read coresight register over Virtual interface. The Virtual read coresight register function for SPSDK library to support various DEBUG PROBES. - :param access_port: if True, the Access Port (AP) register will be read(defau1lt), otherwise the Debug Port + :param access_port: if True, the Access Port (AP) register will be read(default), otherwise the Debug Port :param addr: the register address :return: The read value of addressed register (4 bytes) :raises DebugProbeTransferError: The IO operation failed @@ -268,7 +270,7 @@ def reset(self) -> None: def clear(self, only_substitute: bool = False) -> None: """Clear the buffered values. - :param only_substitute: When set, it clers just substitute data. + :param only_substitute: When set, it clears just substitute data. """ if not only_substitute: self.coresight_dp.clear() @@ -299,7 +301,7 @@ def set_coresight_dp_substitute_data(self, substitute_data: Dict) -> None: self.coresight_dp_substituted = substitute_data def set_coresight_ap_substitute_data(self, substitute_data: Dict) -> None: - """Set the coresigth AP read substitute data. + """Set the coresight AP read substitute data. :param substitute_data: Dictionary of list of substitute data. """ @@ -315,7 +317,7 @@ def dp_write_cause_exception(self) -> None: def _load_subs_from_param(self, arg: str) -> Dict: """Get the substituted values from input arguments. - :param arg: Input string arguments with substitude values. + :param arg: Input string arguments with substitute values. :return: List of values for the substituted values. :raises DebugProbeError: The input string is not able do parse. """ diff --git a/tests/debuggers/test_debug_probe virtual.py b/tests/debuggers/test_debug_probe virtual.py deleted file mode 100644 index 1d54b26e..00000000 --- a/tests/debuggers/test_debug_probe virtual.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 2021 NXP -# -# SPDX-License-Identifier: BSD-3-Clause -""" Tests for Virtual Debug Probe.""" -import pytest - -from spsdk.debuggers.debug_probe import ( - DebugProbeError, - DebugProbeNotOpenError, - DebugProbeMemoryInterfaceNotEnabled, - DebugProbeTransferError - ) -from tests.debuggers.debug_probe_virtual import DebugProbeVirtual - - -def test_virtualprobe_basic(): - vp = DebugProbeVirtual("ID", None) - assert vp is not None - assert vp.hardware_id == "ID" - - assert not vp.opened - vp.open() - assert vp.opened - vp.close() - assert not vp.opened - -def test_virtualprobe_dp(): - vp = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): - vp.coresight_reg_read(False, 0) - with pytest.raises(DebugProbeNotOpenError): - vp.coresight_reg_write(False, 0, 0) - vp.open() - - assert vp.coresight_reg_read(False, 0) == 0 - vp.coresight_reg_write(False, 0, 1) - assert vp.coresight_reg_read(False, 0) == 1 - - vp.coresight_reg_write(False, 0, 1) - assert vp.coresight_reg_read(False, 0) == 1 - - vp.set_coresight_dp_substitute_data({0:[2, 3, "Exception", "Invalid"]}) - assert vp.coresight_reg_read(False, 0) == 2 - assert vp.coresight_reg_read(False, 0) == 3 - with pytest.raises(DebugProbeError): - assert vp.coresight_reg_read(False, 0) == 3 - - assert vp.coresight_reg_read(False, 0) == 1 - assert vp.coresight_reg_read(False, 0) == 1 - - vp.dp_write_cause_exception() - - with pytest.raises(DebugProbeTransferError): - vp.coresight_reg_write(False, 0, 0) - -def test_virtualprobe_ap(): - vp = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): - vp.coresight_reg_read(True, 0) - - with pytest.raises(DebugProbeNotOpenError): - vp.coresight_reg_write(True, 0, 0) - - vp.open() - - assert vp.coresight_reg_read(True, 0) == 0 - vp.coresight_reg_write(True, 0, 1) - assert vp.coresight_reg_read(True, 0) == 1 - - vp.coresight_reg_write(True, 0, 1) - assert vp.coresight_reg_read(True, 0) == 1 - - vp.set_coresight_ap_substitute_data({0:[2, 3, "Exception", "Invalid"]}) - assert vp.coresight_reg_read(True, 0) == 2 - assert vp.coresight_reg_read(True, 0) == 3 - with pytest.raises(DebugProbeError): - assert vp.coresight_reg_read(True, 0) == 3 - assert vp.coresight_reg_read(True, 0) == 1 - assert vp.coresight_reg_read(True, 0) == 1 - -def test_virtualprobe_debugmbox(): - vp = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): - vp.dbgmlbx_reg_read(0) - with pytest.raises(DebugProbeNotOpenError): - vp.dbgmlbx_reg_write(0, 0) - - vp.open() - - assert vp.dbgmlbx_reg_read(0) == 0 - vp.dbgmlbx_reg_write(0, 1) - assert vp.dbgmlbx_reg_read(0) == 1 - - vp.dbgmlbx_reg_write(0, 1) - assert vp.dbgmlbx_reg_read(0) == 1 - - vp.set_coresight_ap_substitute_data({0x02000000:[2, 3]}) - assert vp.dbgmlbx_reg_read(0) == 2 - assert vp.dbgmlbx_reg_read(0) == 3 - assert vp.dbgmlbx_reg_read(0) == 1 - -def test_virtualprobe_memory(): - vp = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): - vp.mem_reg_read(0) - - with pytest.raises(DebugProbeNotOpenError): - vp.mem_reg_write(0, 0) - - vp.open() - with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): - vp.mem_reg_read(0) - with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): - vp.mem_reg_write(0, 0) - - vp.enable_memory_interface() - - assert vp.mem_reg_read(0) == 0 - vp.mem_reg_write(0, 1) - assert vp.mem_reg_read(0) == 1 - - vp.mem_reg_write(0, 1) - assert vp.mem_reg_read(0) == 1 - - vp.set_virtual_memory_substitute_data({0:[2, 3, "Exception", "Invalid"]}) - assert vp.mem_reg_read(0) == 2 - assert vp.mem_reg_read(0) == 3 - with pytest.raises(DebugProbeError): - assert vp.mem_reg_read(0) == 3 - assert vp.mem_reg_read(0) == 1 - assert vp.mem_reg_read(0) == 1 - -def test_virtualprobe_reset(): - vp = DebugProbeVirtual("ID", None) - with pytest.raises(DebugProbeNotOpenError): - vp.reset() - vp.open() - vp.reset() - -def test_virtualprobe_init(): - with pytest.raises(DebugProbeError): - vp = DebugProbeVirtual("ID", {"exc":None}) - - vp = DebugProbeVirtual("ID", {"subs_ap":'{"0":[1,2]}', "subs_dp":'{"0":[1,2]}', "subs_mem":'{"0":[1,2]}'}) - assert vp.coresight_ap_substituted == {0:[2, 1]} - assert vp.coresight_dp_substituted == {0:[2, 1]} - assert vp.virtual_memory_substituted == {0:[2, 1]} - vp.clear(True) - vp.clear(False) - -def test_virtualprobe_init_false(): - with pytest.raises(DebugProbeError): - DebugProbeVirtual("ID", {"subs_ap":'{"0":1,2]}'}) diff --git a/tests/debuggers/test_debug_probe.py b/tests/debuggers/test_debug_probe.py index ea045135..37f3696c 100644 --- a/tests/debuggers/test_debug_probe.py +++ b/tests/debuggers/test_debug_probe.py @@ -10,15 +10,18 @@ import spsdk.debuggers.debug_probe as DP def test_probe_ap_address(): + """Test of Debug Probe Interface - Test get AP index from address.""" assert DP.DebugProbe.get_coresight_ap_address(8, 8) == 0x08000008 with pytest.raises((spsdk.SPSDKError, ValueError)): assert DP.DebugProbe.get_coresight_ap_address(256, 8) == 0xFF000008 def test_probe_get_connected_probes(): + """Test of Debug Probe Interface - Test getting of connected probes.""" with pytest.raises(NotImplementedError): DP.DebugProbe.get_connected_probes() def test_probe_not_implemented(): + """Test of Debug Probe Interface - Test of none implemented API.""" probe = DP.DebugProbe("ID", None) with pytest.raises(NotImplementedError): probe.get_connected_probes() diff --git a/tests/debuggers/test_debug_probe_utils.py b/tests/debuggers/test_debug_probe_utils.py index 9c2ca33d..a79e3e2b 100644 --- a/tests/debuggers/test_debug_probe_utils.py +++ b/tests/debuggers/test_debug_probe_utils.py @@ -8,11 +8,16 @@ import pytest from tests.debuggers.debug_probe_virtual import DebugProbeVirtual -from spsdk.debuggers.utils import DebugProbeUtils, DebugProbes, ProbeDescription +from spsdk.debuggers.utils import ( + DebugProbeUtils, + DebugProbes, + ProbeDescription, + ProbeNotFoundError +) import spsdk.debuggers.debug_probe as DP def test_debugprobes_append(): - + """Test of Debug Probe Utilities - Append to list.""" probe_list = DebugProbes() probe_descr = ProbeDescription("None", "None", "None", DP.DebugProbe) probe_list.append(probe_descr) @@ -23,7 +28,7 @@ def test_debugprobes_append(): probe_list.append("Invalid Type") def test_debugprobes_insert(): - + """Test of Debug Probe Utilities - Insert to list.""" probe_list = DebugProbes() probe_descr = ProbeDescription("None", "None", "None", DP.DebugProbe) probe_list.insert(0, probe_descr) @@ -34,6 +39,7 @@ def test_debugprobes_insert(): probe_list.insert(0, "Invalid Type") def test_debugprobes_discovery(): + """Test of Debug Probe Utilities - Discovery probes.""" probe_list = DebugProbeUtils.get_connected_probes("virtual", DebugProbeVirtual.UNIQUE_SERIAL) assert probe_list.pop().description == "Special virtual debug probe used for product testing" @@ -42,6 +48,7 @@ def test_debugprobes_discovery(): assert len(probe_list) == 0 def test_debugprobes_get_probe(): + """Test of Debug Probe Utilities - Get probe.""" probe_list = DebugProbeUtils.get_connected_probes("virtual", DebugProbeVirtual.UNIQUE_SERIAL) probe = probe_list.select_probe().get_probe() @@ -49,3 +56,19 @@ def test_debugprobes_get_probe(): with pytest.raises(DP.DebugProbeError): assert probe_list.select_probe().get_probe({"exc":None}) is None + +def test_debugprobes_select_probe(): + """Test of Debug Probe Utilities - Select probe.""" + probe_list = DebugProbes() + + with pytest.raises(ProbeNotFoundError): + probe_list.select_probe(silent=True) + + with pytest.raises(ProbeNotFoundError): + probe_list.select_probe(silent=False) + + probe_description = ProbeDescription("virtual", DebugProbeVirtual.UNIQUE_SERIAL, "Virtual Probe", DebugProbeVirtual) + probe_list.append(probe_description) + + assert probe_list.select_probe(silent=True) == probe_description + assert probe_list.select_probe(silent=False) == probe_description diff --git a/tests/debuggers/test_debug_virtual_probe.py b/tests/debuggers/test_debug_virtual_probe.py new file mode 100644 index 00000000..2937ab84 --- /dev/null +++ b/tests/debuggers/test_debug_virtual_probe.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +""" Tests for Virtual Debug Probe.""" +import pytest + +from spsdk.debuggers.debug_probe import ( + DebugProbeError, + DebugProbeNotOpenError, + DebugProbeMemoryInterfaceNotEnabled, + DebugProbeTransferError +) +from tests.debuggers.debug_probe_virtual import DebugProbeVirtual + + +def test_virtualprobe_basic(): + """Test of virtual Debug Probe - Basic Test.""" + virtual_probe = DebugProbeVirtual("ID", None) + assert virtual_probe is not None + assert virtual_probe.hardware_id == "ID" + + assert not virtual_probe.opened + virtual_probe.open() + assert virtual_probe.opened + virtual_probe.close() + assert not virtual_probe.opened + +def test_virtualprobe_dp(): + """Test of virtual Debug Probe - Debug port access.""" + virtual_probe = DebugProbeVirtual("ID", None) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.coresight_reg_read(False, 0) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.coresight_reg_write(False, 0, 0) + virtual_probe.open() + + assert virtual_probe.coresight_reg_read(False, 0) == 0 + virtual_probe.coresight_reg_write(False, 0, 1) + assert virtual_probe.coresight_reg_read(False, 0) == 1 + + virtual_probe.coresight_reg_write(False, 0, 1) + assert virtual_probe.coresight_reg_read(False, 0) == 1 + + virtual_probe.set_coresight_dp_substitute_data({0:[2, 3, "Exception", "Invalid"]}) + assert virtual_probe.coresight_reg_read(False, 0) == 2 + assert virtual_probe.coresight_reg_read(False, 0) == 3 + with pytest.raises(DebugProbeError): + assert virtual_probe.coresight_reg_read(False, 0) == 3 + + assert virtual_probe.coresight_reg_read(False, 0) == 1 + assert virtual_probe.coresight_reg_read(False, 0) == 1 + + virtual_probe.dp_write_cause_exception() + + with pytest.raises(DebugProbeTransferError): + virtual_probe.coresight_reg_write(False, 0, 0) + +def test_virtualprobe_ap(): + """Test of virtual Debug Probe - Access port control.""" + virtual_probe = DebugProbeVirtual("ID", None) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.coresight_reg_read(True, 0) + + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.coresight_reg_write(True, 0, 0) + + virtual_probe.open() + + assert virtual_probe.coresight_reg_read(True, 0) == 0 + virtual_probe.coresight_reg_write(True, 0, 1) + assert virtual_probe.coresight_reg_read(True, 0) == 1 + + virtual_probe.coresight_reg_write(True, 0, 1) + assert virtual_probe.coresight_reg_read(True, 0) == 1 + + virtual_probe.set_coresight_ap_substitute_data({0:[2, 3, "Exception", "Invalid"]}) + assert virtual_probe.coresight_reg_read(True, 0) == 2 + assert virtual_probe.coresight_reg_read(True, 0) == 3 + with pytest.raises(DebugProbeError): + assert virtual_probe.coresight_reg_read(True, 0) == 3 + assert virtual_probe.coresight_reg_read(True, 0) == 1 + assert virtual_probe.coresight_reg_read(True, 0) == 1 + +def test_virtualprobe_debugmbox(): + """Test of virtual Debug Probe - Debug mailbox API.""" + virtual_probe = DebugProbeVirtual("ID", None) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.dbgmlbx_reg_read(0) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.dbgmlbx_reg_write(0, 0) + + virtual_probe.open() + + assert virtual_probe.dbgmlbx_reg_read(0) == 0 + virtual_probe.dbgmlbx_reg_write(0, 1) + assert virtual_probe.dbgmlbx_reg_read(0) == 1 + + virtual_probe.dbgmlbx_reg_write(0, 1) + assert virtual_probe.dbgmlbx_reg_read(0) == 1 + + virtual_probe.set_coresight_ap_substitute_data({0x02000000:[2, 3]}) + assert virtual_probe.dbgmlbx_reg_read(0) == 2 + assert virtual_probe.dbgmlbx_reg_read(0) == 3 + assert virtual_probe.dbgmlbx_reg_read(0) == 1 + +def test_virtualprobe_memory(): + """Test of virtual Debug Probe - Memory access tests.""" + virtual_probe = DebugProbeVirtual("ID", None) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.mem_reg_read(0) + + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.mem_reg_write(0, 0) + + virtual_probe.open() + with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): + virtual_probe.mem_reg_read(0) + with pytest.raises(DebugProbeMemoryInterfaceNotEnabled): + virtual_probe.mem_reg_write(0, 0) + + virtual_probe.enable_memory_interface() + + assert virtual_probe.mem_reg_read(0) == 0 + virtual_probe.mem_reg_write(0, 1) + assert virtual_probe.mem_reg_read(0) == 1 + + virtual_probe.mem_reg_write(0, 1) + assert virtual_probe.mem_reg_read(0) == 1 + + virtual_probe.set_virtual_memory_substitute_data({0:[2, 3, "Exception", "Invalid"]}) + assert virtual_probe.mem_reg_read(0) == 2 + assert virtual_probe.mem_reg_read(0) == 3 + with pytest.raises(DebugProbeError): + assert virtual_probe.mem_reg_read(0) == 3 + assert virtual_probe.mem_reg_read(0) == 1 + assert virtual_probe.mem_reg_read(0) == 1 + +def test_virtualprobe_reset(): + """Test of virtual Debug Probe - Reset API.""" + virtual_probe = DebugProbeVirtual("ID", None) + with pytest.raises(DebugProbeNotOpenError): + virtual_probe.reset() + virtual_probe.open() + virtual_probe.reset() + +def test_virtualprobe_init(): + """Test of virtual Debug Probe - Initialization.""" + with pytest.raises(DebugProbeError): + virtual_probe = DebugProbeVirtual("ID", {"exc":None}) + + virtual_probe = DebugProbeVirtual( + "ID", + {"subs_ap":'{"0":[1,2]}', "subs_dp":'{"0":[1,2]}', "subs_mem":'{"0":[1,2]}'} + ) + assert virtual_probe.coresight_ap_substituted == {0:[2, 1]} + assert virtual_probe.coresight_dp_substituted == {0:[2, 1]} + assert virtual_probe.virtual_memory_substituted == {0:[2, 1]} + virtual_probe.clear(True) + virtual_probe.clear(False) + +def test_virtualprobe_init_false(): + """Test of virtual Debug Probe - Invalid Initialization.""" + with pytest.raises(DebugProbeError): + DebugProbeVirtual("ID", {"subs_ap":'{"0":1,2]}'}) diff --git a/tests/image/misc/test_dict_diff.py b/tests/image/misc/test_dict_diff.py index f3c2bffe..79e7cae2 100644 --- a/tests/image/misc/test_dict_diff.py +++ b/tests/image/misc/test_dict_diff.py @@ -1,21 +1,29 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause +"""Test of dictionary compare.""" from spsdk.image.misc import dict_diff - -def test_simle_diff(): +def test_simple_diff(): + """ Simple test of dictionary compare with integers.""" main = {'a': '0b00', 'b': '0b01'} mod = {'b': '0b11'} assert dict_diff(main, mod) == {'b': '0b11'} +def test_simple_diff_strings(): + """ Simple test of dictionary compare with integers.""" + main = {'a': '0b00', 'b': 'Hello', 'c': 'Good Morning'} + mod = {'b': 'Cao', 'c': 'Good Morning'} + + assert dict_diff(main, mod) == {'b': 'Cao'} def test_nested_diff(): + """ Simple test of dictionary compare with nested dictionaries.""" main = { "BOOT_CFG": { "DEFAULT_ISP_MODE": "0b000", @@ -39,7 +47,7 @@ def test_nested_diff(): "BOOT_FAILURE_PIN": "0x0" }, "CUSTOMER_DEFINED0": "0x0000", - "CUSTOMER_DEFINED2": "0x0000", + "CUSTOMER_DEFINED1": "0x0000", "CUSTOMER_DEFINED2": "0xABCD", "CUSTOMER_DEFINED3": "0xEF01", "ThisOneDoesn'tExists": "0x00" diff --git a/tests/image/misc/test_format_value.py b/tests/image/misc/test_format_value.py index 2e6ddbef..3ae469bb 100644 --- a/tests/image/misc/test_format_value.py +++ b/tests/image/misc/test_format_value.py @@ -6,21 +6,7 @@ # SPDX-License-Identifier: BSD-3-Clause import io import pytest -from spsdk.image.misc import format_value, read_raw_data, NotEnoughBytesException - - -@pytest.mark.parametrize( - "value,size,expected", - [ - (0, 2, "0b00"), (0, 4, "0b0000"), (0, 10, "0b00_0000_0000"), - (0, 8, "0x00"), (0, 16, "0x0000"), - (0, 32, "0x0000_0000"), - (0, 64, "0x0000_0000_0000_0000") - ] -) -def test_format_value(value, size, expected): - assert format_value(value, size) == expected - +from spsdk.image.misc import read_raw_data, NotEnoughBytesException def test_read_raw_segment(): stream = io.BytesIO() diff --git a/tests/mboot/blhost/test_blhost_cli.py b/tests/mboot/blhost/test_blhost_cli.py index f0bf5142..db25fead 100644 --- a/tests/mboot/blhost/test_blhost_cli.py +++ b/tests/mboot/blhost/test_blhost_cli.py @@ -34,8 +34,13 @@ # efuse-read-one 0x98 with unknown error code 0xbeef (48,879) b'\x5a\xa4\x0c\x00\x2e\x7b\x0f\x00\x00\x02\x98\x00\x00\x00\x04\x00\x00\x00': b'\x5a\xa1\x5a\xa4\x0c\x00\x36\xc2\xaf\x00\x00\x02\xef\xbe\x00\x00\x00\x00\x00\x00', + + # use get-property 99 as a vehicle to emulate no response from target + b'\x5a\xa4\x0c\x00\x55\x31\x07\x00\x00\x02\x63\x00\x00\x00\x00\x00\x00\x00': + b'' } + def test_version(): runner = CliRunner() result = runner.invoke(blhost.main, ['--version']) @@ -77,3 +82,14 @@ def test_efuse_read_once_unknown_error(caplog): assert result.exit_code == 0 assert 'Response word 1 = 4 (0x4)' not in result.output assert 'Unknown error code' in result.output + + +def test_no_response(caplog): + caplog.set_level(100_000) + runner = CliRunner() + # use get-property 99 as a vehicle to emulate no response from target + cmd = '-p super-com get-property 99' + with patch('spsdk.mboot.interfaces.uart.Serial', SerialProxy.init_proxy(data_responses)): + result = runner.invoke(blhost.main, cmd.split()) + assert result.exit_code == 0 + assert 'Response status = 10004 (0x2714) No response packet from target device.' in result.output diff --git a/tests/mboot/blhost/test_blhost_utils.py b/tests/mboot/blhost/test_blhost_utils.py index 347a8a04..3792110a 100644 --- a/tests/mboot/blhost/test_blhost_utils.py +++ b/tests/mboot/blhost/test_blhost_utils.py @@ -8,7 +8,7 @@ """Testing utilities for the BLHost application.""" import pytest -from spsdk.apps.blhost_helper import parse_property_tag +from spsdk.apps.blhost_helper import parse_key_prov_key_type, parse_property_tag @pytest.mark.parametrize( @@ -22,3 +22,16 @@ def test_parse_property_tag(input, expected): actual = parse_property_tag(input) assert actual == expected + + +@pytest.mark.parametrize( + 'input, expected', + [ + ('1', 1), ('0xa', 10), ('0b100', 4), + ('abc', 0xFF), ('012', 0xFF), ('some-nonsense', 0xFF), + ('sbkek', 3), ('UDS', 12) + ] +) +def test_parse_key_prov_key_type(input, expected): + actual = parse_key_prov_key_type(input) + assert actual == expected diff --git a/tests/mboot/test_mboot_api.py b/tests/mboot/test_mboot_api.py index 0c5f7ccb..ea785009 100644 --- a/tests/mboot/test_mboot_api.py +++ b/tests/mboot/test_mboot_api.py @@ -6,7 +6,8 @@ # SPDX-License-Identifier: BSD-3-Clause import pytest -from spsdk.mboot.mcuboot import PropertyTag, StatusCode, McuBootConnectionError, CmdPacket, CommandTag, ExtMemId +from spsdk.mboot.mcuboot import PropertyTag, StatusCode, CmdPacket, CommandTag, ExtMemId +from spsdk.mboot.exceptions import McuBootCommandError, McuBootConnectionError from spsdk.mboot.mcuboot import KeyProvUserKeyType, McuBoot from spsdk.mboot.error_codes import StatusCode @@ -51,8 +52,15 @@ def test_cmd_read_memory_data_abort(mcuboot, target): def test_cmd_read_memory_timeout(mcuboot, target): mcuboot._device.fail_step = 0 - with pytest.raises(McuBootConnectionError): + mcuboot.read_memory(0, 100) + assert mcuboot.status_code == StatusCode.NO_RESPONSE + + mcuboot._cmd_exception = True + with pytest.raises(McuBootCommandError) as exc_info: mcuboot.read_memory(0, 100) + mcuboot._cmd_exception = False + assert exc_info.value.error_value == StatusCode.NO_RESPONSE + def test_cmd_write_memory(mcuboot, target): data = b'\x00' * 100 diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_keystore.sb.txt index ca8dd315..14dc1253 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800e20300005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -91fd92b569f71fc9f4f1041d2159de849940eaee06bb4a9dbf8392076b1dde60 +b1502465b63980793edb2eaeb150f51b6a956a5c9a05fd0072405f986e9d3047 [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -85a679db6da1d74fdae06defcf64ef2a32c5efac6a3ba72bc26632a4323a4c1ee6933a2af9a4ce6e390a162b2f490c329e8c3e715f8c8fc3f046e38da04c724924dcf744c5260b97a85aff0f506df313b6ce07b659a0ddcaef40da37bb66f9caa6a79cff453a64a7dd6b046f7bb12eed237f1b5ac14076e33c5bf5e3efad1a55c7ab86443077b0400947c346e10ab0ce7489de50a0297e1c9a97479cab7b9a839365ca1477aba747de1481588755550ecb3cbe9a81076e46be89ab8a65e1d427be8d86cf7ec97915fc53c96c585cc5430c233e8d3e88f8b3bc27e02de6ad061c9c6a0f589b21e6a1efd8554e62a42e043d75f443c3865906bad6bb3daaed8af7 +17f81eec317f20fd2b385447ee1d1a8c3e442cfcb80f94175b85324e5b7601bab696936332cec5f3fe512681269e8fb9652662646c0ce6132ad9c323a706e4a269eedee1c9a72e2c1c147b2b525739a243add60de80b8d25c24eb8c1f60e20f6532786bf77ffea125dc6c565275ab130fdee2d7d3f98be60d1c7a7c32738919e4c6da4b01e23c53a221263f39680e6b3f5871893b4a9619027714bae6509ea403b09dafc1fce99822fe48e34f9ca05c4d13600711f5f9a3de03ba38007b972b269e674a5e49a144250785e9823840bb455d7f88bbc2f1fed050b9920367e7c4244d23a3c03b6ceca491afe84164aa13084511c048b20d8cda52de00ff8a2a145 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ f6693498895ccbd880ca0dd280ce8681 644f4aee4b3b2ec2eb12a2bb8aec14898f82e1c73eb84417abc689add38262a6 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_otp.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_otp.sb.txt index 4fef82f6..61becee5 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_otp.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_crc_otp.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800e30300005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -b53ded0421fdb9a4a71fd0208eda88e3989705d93a94af01bebb2ac4385ea5a6 +95a5ae368fb4b3a4b69f4a00d96c5d763803e5a8d6a34ce615d548e48f00013a [key_blob] 4a273c8f321743a44f3bf244ea1b8c30af9b93e30fe1fc7a2895240f14b16df4443c8a40303c89294fed1800170fdbf8da4bee1bf6b78c476a32ed27e53767df03e13fae4a5106800000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -00646dafbc1b7f86a4744646c95c9034394dd9646f0b5def33b1b7dd2d9c5a9ed0db77121321bb91b256f467b4334c9b0737286feb36deb32ffe1d0d3e728c9db6547f2943a554f9826d00b1bc30c3832c3fa45b9f6f63f8caa1bca9b09835fe99c3fa28fb8c9aeb9f1c6cce6cc835f48aba9700257a64ef09585e9906e5d9ce7cf0eb22a39fbfd37b3f303818b74fead32fe02c37865f138adbffd18e78399bb757a593bbae9caa22fa3e12bec6c742034756290c0a48669e8a8f3d8980f670cbb90688322e1fd5721a1edc424a6b62a813ecdac5a35a1f2b7676d3b61d548ca6eeb82610a077a227ba37acaffced145cebc6358c6b064d73d66d3e35c0dd8c +c52b2bc454f8dec929549a8297d9db7a9956a1c2d35c4206c176eda4539b40225350bc2bb4a94e0c3561da54e780c28a98ef445d21e8870391ca76d4432663b0931a93b3a9019ecec332d4f1a99601dc1b0cc110138d76e0d5654746fa4a75b2f5fa24d17fed8c1eff9965329548f30935dba584c6afecadfb7c9a3149232a0d60d759b11eb6bcc347fb6be80be79484d7b9f58e428747114d6f691728d8a55e44717027b779b5fdff7209d6560b18a38418dfbe3764a3033a3c62f2e415e337fa294e2217e35676310bdc52709d8a688d39c3d9b26cc232792905ab8e8191024625ed720a6a074ec977947272a39d8e5c987f5f9d1338862efb37b7154eb061 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ f5693498895ccbd883ca0dd280ce8681 d5df24b58dc0e6200a101424804475aabfd3f88b88e0f240bef012fe1595a65d [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_encr_keystore_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_encr_keystore_keystore.sb.txt index 14b34f47..5e50e199 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_encr_keystore_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_encr_keystore_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d50020108008d0400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -d5a6360ca750f1d26163f456f26375f11831ecfd0e3038ddfcac5c24661331cd +317e25b2545d763ff0a17c2334ae6c9b9e0da45b2a4d3233eeb153c29a1e06d4 [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -c849f12f72c23a2f2897c46954ad3f14e4268f6c36b44f93cee16086048f9624f55dd45b82205c5d7227e5bbefdc47497f8b4a17e7ec80c5254e1e873722c56acd086b81c3d71b4444c8c5a0b6a3740aef1846d6fdad663e10a354c453bdb02907ac797b316c781536dcf753eb393f6f85819741cccf75bf15f6a12a9a993ebd9a6eb832e85dae127f86fde2ded81e9b213c8381e555c532625c4bc4b459ed6f9f90b4281fd80ccc19ae11ff42ed4da7c376a6f5d49347aa86c21f2e9331938dca327080457b4adf716d8100939744faeb33e2ba447597c22ff0c1db3d0382135730aae1934b71c6493189d459e708d5e7f056fe888d1a5bfb19d22f52ac41c6 +1987661bb39f7ac2a7d963776c165bb24bc40cc8e1620a4e7cc346c932ee8ecb8cc859d8982160631e6993ebb009410cd5034b3a0016830d2ffa712792ca05ca4e9d6c2b475dd012c4c54d272a101b533aedefb21693be21c1ea92527fe84e918b6c76a58fc3827c9933dd57bb2e471cd0d818d5f21f6da9aa744b4cb3a1d810cd9162992ea26160ade1c4a141fe9602f380b334d57b2fa33639574d87ca378fb497a90fa5b18f101f7254c41d12c33aea28a5478ff478806804c2bc33c322ca6c5a530a36fe46f4bab79906c66b57480b54e16438ae2c12a859f3103421b36311a1779724ba09530f7dcecb9f8a199299fcc452924e7a8f14937c695dd4f2f2 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ c849f12f72c23a2f2897c46954ad3f14e4268f6c36b44f93cee16086048f9624f55dd45b82205c5d 9584da9bc060dc1aba773cd3b2daca0cc9368772268645475af9c014486b03a4 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_signed_keystore_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_signed_keystore_keystore.sb.txt index 63e14fce..7966a9b1 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_signed_keystore_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_signed_keystore_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800880400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -6c5a95a2b00011c84496df1cbbde48299bba9932e7f321a0000a1a43b7485321 +4524c3ace5eefb099cfa12daf6cc0dce328ce293dab61a8bcf0e274c1dee09bd [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -aad9220cb17969afab5b152f67bc3c3884334892f74e6f972544f7f34eb348f5f74a9142a431d4ac957cebde1af3062e627bba301ce3c63edc1186cacb8053d6ba36a5f760c43f7378aea00c36f10fd767ee4fe981ac297233c35fcbfc4523ab91dbc4dc3eda6e2053008745d17b754e064f7012a3d12431dbd6a3d1d9126122121795592a286bfc306b7d45fce036c7203316e90472e449767affa05ed3ffc3a24395f9c6dd1103990aa1594a9511c8dc4696d8dec110aecbd1455f717afff6969dbe9ca90d4fca17ca0ef83c9df4fbf1c66a07ee48f965bcd47eff7521c7d21dfc429516346d7c7668837f4b7c24c0245b58abd3e10753aaf4ec8246967bb5 +b8ca5734740a3eef70283560dbe527d024521a758dd88f4c848813f375be05667b7b4b89339c9c24633541cfb1a2375885a8809b09528b0e14d08f51dd1f0848a8c971c3975fbbe918c2fa05129172db9ab97f22f6982c2fd0932e94a87f9dc27551172f899b514b756aee15c5cb73429836b4f05027026634bfa439536859e65fcb1d1cf3cc3ba5bd66538045c0a98ad2046c627683d992f46278370e3180984373463a2cebb702b703789b577108fd77ff1988df37673b3e86c4bd2bff0a6e286b43df030d48bbca651e0e6d3bf47f3236e5f31de9b07b6f17686c13cd80669e41b1efdd545ac4a854bf3f9519a3ac3cb6e42dde8f9ee8e80e4bd75084a4e5 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ aad9220cb17969afab5b152f67bc3c3884334892f74e6f972544f7f34eb348f5f74a9142a431d4ac 82c2f807ea8d220c226dd34c49727a455c4f3b666c0fb2453b96ebec0195aa40 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_keystore.sb.txt index 73511b05..d7d62682 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800e20300005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -630d519494e5e2f28ef3979fb253b60433e66d38cbf13ffb9d8421c54157d73c +1716607c1596e339fb8bfe75d5ca0bd9ea23b917d5b65632a4dc906ee4e9c9b5 [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -2c5294f6daaebb7a03b1a504a23bd0760d04b023ff48ea91254f2fb83925be9d1c5e98425136904fcde5ed4809a62148ddfc60c7e057cec0666fa60a01f457b70fa307f55cca5aa3bc605905f1364a3bba1b48b06183785e168af898a1bbf2a6e589483aed433a0be5d9ded5b319762a5f7e503495ab502a84b2359cef1422f6cba649bcf325a6e12d3e638cd8d8d554414344fe0618c3539a406a4a7e52146499ea394b0c275357881d4dd5da2254c7b152f7e6e8254aeaa40aad6290d3d7abc113b1072e57fb58a61666ce2e06b9560c1dbcbe5ca067fb6081bc0d6fe6ecf202f27dbeee6146b7f1546bc3f8eeec1af7b8fefad9581a0618b6b65f703f9557 +678d1e91a81a73b878b948ec1a150fa21e44b9efb38a49f31d6316eec213cf0c652b38e0119682415ac09497fcc165bffcf798fb9b58ad13a67acc60ba53599fc658d199b30e0e0787c96196c9b88f07c57cb3df90aba294019dc278065ed6360c2d1f3b547b60ee42d63f8802987533f82dc2ea82e1b54f36a4709699499a1752e4349399e44a32c80ffadae88a2343e1fba99b6aec9f7fb527e40d2b53a27f7facf3ae42a83990e2d62f13913a2ff9baba8265a9d1ec9bd16f05d1c271c9fd39daf620a5d3aff66e4b217ba0da1e9c8174eb5cd79a595e6abc19a8482f334eacaefe87766960be3b701cee2a2755d0a10dc16dd24e69a21f2efb81d09cd868 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ f6693498895ccbd880ca0dd280ce8681 644f4aee4b3b2ec2eb12a2bb8aec14898f82e1c73eb84417abc689add38262a6 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_keystore.sb.txt index 1cb4f387..4c698115 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d50020108004d0400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -2bafefd7635d54e56ef28e0aabe9052937dd3a656bbb7ff6936b01992f9f709d +30e4f3a03fa2155a70d6f4e2113abf5d8a7446f67c3819aa36d927c83718bca3 [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -339d70dce7e7e39bdb1783d6964303a822a9e0136737a7d58b8e942ac2adb049147e8837ff83d1de57ebab32fd75cfe944c7cc063c2c64fd42abca4af0ea1b5eff1f0a620c0520fda920c04eca23ccf3030b903322f5cbe51d3b4765f27dc99da1311ea1fedb8745d780a18a2c7a23f0f04c54c6609ccc88fa191b3c0a8c8f34793b81fd9e93048ed9a813927987193f24f22a255d460649c2f844a9f97a7362e806ed040e2667e2414042cc03bbdf114e00990879b58867e9e844f32a096d24f43f70c7b59ebcc24fb9098626e8054c987ce97aa35724355b7d98884610155021238673648553874abd3cdcbc692f7654525a7efddac1dfd7e24c66b7cbf21f +0e0c1a616fd60931209c1b567c77eefb558997ee8f0f1b72dde7b5eb412c82b3f9fb227fc85dc636511d75e8668aba3dd15eaf3debf226fa61b7880fb60c46ac4986a876c66320c3cc2fafb2fbd26c8db844b8cf45d34b40083efd5368e56a565d9bd4f412bb039a208e0a5eea3a14d2435934c48a961b274cf9989b6d4fc0edf8b21cfdf0d91e6708ba284dbaf50f045e669523ad5ab3437c4377cb9acfa767d703b7e1c169ec8793977a32288a3ba6f7abaebadaf30f55f00f375761dde5bb73e99769eea74b908109265e9868d67eeb0678e7c3910c92d57fdc1583add6b9873eeccb79c8a547e77765d064e4e1ae8d2cde9b4ad5fd8f08d6211c2192b09a [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ d001018000000000f003000001000000 525d9112b66d43653df4573983fe4ba4000b9573c221974f28236e9ac075d48d [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_otp.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_otp.sb.txt index d9edd769..13999ab3 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_otp.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otfad_otp.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d50020108004d0400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -59cc40c4d300cbb1c1e314f6fc38d639c9c69ec6d20f7946bde5c05e12a9164b +fc25077ef4c6bcf55f5deaff07f27d62ce78c1a6799e2cb5e59a34db87fe9155 [key_blob] 4a273c8f321743a44f3bf244ea1b8c30af9b93e30fe1fc7a2895240f14b16df4443c8a40303c89294fed1800170fdbf8da4bee1bf6b78c476a32ed27e53767df03e13fae4a5106800000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -8f1aedbfca2de8131bf9b963349bedbf968def9e91e123b95fa9f1de987201b69c3afca06f8016142fedc3b6327c5b884d3be4476bd05169b77088364202103fdc64da18fe407babbff12010d60ebd8dd920dfd0bf0e5007e6caad48613cd6fee6630a82844e9bffac1a584cec233728aea7f0bc61c60222fe00a8e920c81b3e4afa9a0244b437d379026837b8af64f84e0eae588752b3436254204b1903026eaa4e7c4ec9044008da080618ccafacbf74b7ddfbded97af54796ea8d2826b21cd96cd3c2b275b9f25f7c719321d9a705b44806fba0454fb5d21ad524cdf727fc42270c701979ef78a6be1bbb6ad489278ca2a5d4e1e1232242b72fea43eba9e2 +b54fd1c7f7d87ddd2301eb780416b695b395cae8e7dc8ee0b79bebefdda53c8c765e05986bbbae8129b12465136fe9cb0c02afbb4894c0f6dcfb2b2f909f9950f1b648f567d3d35f0e81d770356ab5376e0bba07ae572f8073f261c8922c125e5f06004539639ed27c232aab112c018df6f141e35a92a0c21c6890e93ea93eebf75d41621bb1c08d1752ba6680d6b2b30d277acd3cbd08f02ed969e55dc747b24fcf28bff6b3c8b0ecf8e1ff3e6364499a253628d54b8ee30a08f7ce18e2d6b7787fc6a54db0451ade7527ad91497d4051d9a90724bdbb602325b86395305ec20494881b027712156c5f3c6f3118b0d2a698043b4e7c422879b2c6a7260f8944 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ d001018000000000f003000001000000 525d9112b66d43653df4573983fe4ba4000b9573c221974f28236e9ac075d48d [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otp.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otp.sb.txt index f2983c3e..c6804235 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otp.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_ram_iar_unsigned_otp.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800e30300005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -2260700751f6232a03b2abbad2ba83ee419217f2931cf95a5899a78b6b3ccaa1 +806f90eebb5bc7323f0b122f21cc3e5a7676d4a114b6bf79a1ae78c25baf9936 [key_blob] 4a273c8f321743a44f3bf244ea1b8c30af9b93e30fe1fc7a2895240f14b16df4443c8a40303c89294fed1800170fdbf8da4bee1bf6b78c476a32ed27e53767df03e13fae4a5106800000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -2126b53c9c5278725452629e5b03a50f8a7df028f7f09e7c842941e3c873b29b47d4a913833f767be2ce6001477ca50e9d7264805ff129be8e415a50071b87cb467bce3e0103ec192e33c2e0585b4c39e796f2a830114bcdb989724ad379c1e6c56f7d43f016a059319ca11700dad8234d8870c50114358bf27322cd22ddfaf31d7c949960a15029291a44e6897ac9fd0dd11878711766d57d4de06ccdc3acc54378faab044efe87fff12f2a26147f0209e3f3d0b92985cc91d974ce9231e10028a7bc6e002bace28a9262f71f600e1149632b7576577ad58f8399d333228dad7e88254aa3fbf89f8172e04ee3b07e0085f6f3d61f774993d15977fda90f2281 +ca1bcbe92abce0d28e2c89d68b20aeac9fc4a5808109befdce01849f42fada85de23a78ab7b318df2122783ef8ec7e59e0e0e052ed2eec17bb41844bae08444469634245e396c5958149f2e247cc4c50a5d03ee1ef0b91e51e392d320d2b92be491ca7c65f24b5684b456af30eb6f0866cebd74b8397887313c599f74d460e98e8db97d5c28a4e7e5ca6dd4e69545636e2fe50eb1d132fabaa707da2c6bccf49ee3a036d4847cd2e0ca997d1accbb2e9afb1578857695651c84b1b47fd0fb59f4e70086d261bc28eefee2bbe2d43ac2c21063fb49e28eeaf2c8ed153e4ecb427711283c55c6e77d052c432da0115dac032632cc04beac2cd707f84bb498acd74 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ f5693498895ccbd883ca0dd280ce8681 d5df24b58dc0e6200a101424804475aabfd3f88b88e0f240bef012fe1595a65d [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_keystore.sb.txt index b275463f..dcf908c0 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800ad0400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -0812fd98de9d527ef9402bc858c2291c480dec9780f15729f553583b5d34973a +8d396c4b94857124a7ba04a13b43ff186bdd7fb18a7dd0aa5ec822578c589b2d [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -0600ef2f84aadf9de471dd31a1fd189c363d51dc26cdd58859b6d06a1772a174a44c0cfb9dad5960471526d72b7d07d8d1baaf472506a7e74f00d0a63ccc47ac03be5727b16405721c08bbab161958ec67ce3974c73fd57bdbe7c1bbc330c6da92f1f4fa3d35e13f0b40c88ac6c8465c044dc20e77ebb93a0aaeda489e2586ed559d9a59be2afbba25997bf207edcdf5f69d496496f2b7b3883b725c2e3f4b3079ecdb3d9978faa4f634f1f2f4735c473cbae67273b81f784edbee7630bf0c7138644084e8efc08dd9b7569ae6062be111ee63a34cf88e82c5f5f5144be3b0f032a37d18c241bbb68fc5efff2bc1b5188c0c4267d583ab2efa1dd26850f2c88a +acc8770d64fdb9e535b8cfc2f4725f6f17f12d4f00b5eac06c8e9c3c747d79b4a918118aaf5a532e6dc63a3d7677c6a3686971bfc90502d5338dbf12d9eee01b14f6ce0362db7a0fa1e58a5435798ec96ba6922ad240be93178cc41065bfe111b4f3113addc574d216f18f49e0d98d130610feeab63b51cb6b69f6babdb1c25ba464d78f393264556adb5b5fbce1a19ac176b3799b264bb105c240a367df182f5d423ee2c0ac18fc0c8be190af6440dce343e89033bcdf67626a173148946e9e4de22083d6823a0349aa24fc00b76aa9ad9ccb71dc32dca19539c73f15fa00a373610f7241ab3829ca4237f8cb902611d162e027c77a682c5e84b63b311daec1 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ a2693498895ccbd855cd0dd280ce8681 0b80eea55f5cf51190e49c36aac6e3eefb651216575e615fea8602705bb27680 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_otp.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_otp.sb.txt index 71d1daa3..c221ca45 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_otp.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_iar_signed_otfad_otp.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800ad0400005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -ab7a8e1061be6c55aba1206b6669a475a26c36159f6b9a1d6578034bbe4c9b6b +6d506813be60d749c91f96ed376674623a2cb5c239872885af20a3eb18af8ce8 [key_blob] 4a273c8f321743a44f3bf244ea1b8c30af9b93e30fe1fc7a2895240f14b16df4443c8a40303c89294fed1800170fdbf8da4bee1bf6b78c476a32ed27e53767df03e13fae4a5106800000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -77bcce70dbf92e5d048bbf51b932fc1f9994587b3648c0a50d0ed11ce6faae3484f91a2d45a9cf01dde531afe8852f5b674f40929e1fc3627d890f951d863d7d4cc8aa2af9fa64302a7b37272dcc35996b93d960f3bd891907ee82145bc9d9bc66ee386ca626e8115b99c496421771d794d98f8199478175a50003b997f4f6d98bd341d08b7db4c8c1ed61aadc9292b63e8b0c4146179299f7131a515883da6bca628e89c7edfd7e6d462e011da8b1bd0cf6ba209650f4005a562054aaeb352288017560894ed68de98c24f662b5a9e04b49a05e8c7e95882e047d6c47dbb9785002acdbb4bd39718bf54cddb27769a745c25fa0cf6839efe2befae1b214f080 +6efb8b9f6cd243f91f54497068c0a59be9d90f09914c887a0746fa0f0142cd5015a3127f9a35585302ce79af30fe6ef0c6d810d624e66d698d3d1b02181f755aa46d26190f3dff5a6ff75c2f75a52034f6b272c8bb29893de786a2e3379d893e883195b20a58b551ed62b0ef31b1a3c9a576713e3da7d5c09cf13bd8ddb6eeeb813561bd4b4e5f647cc56b34f5920ee676a39b65adc188c3f2df81a72b02aed5ac7449f9fb67d17e6f353c6654b24a7b11e31b289deb0f35f58a4a32b0e8384b5467053167fd5cddb6edd0e45144c6d3e486f00c271d8bb02893829241f9aefc676d4270ac7fd3bc0555e14eace0cf51b5810f5f45c720200bfb46fb9b60e5f9 [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ a2693498895ccbd855cd0dd280ce8681 0b80eea55f5cf51190e49c36aac6e3eefb651216575e615fea8602705bb27680 [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_keystore.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_keystore.sb.txt index c3a59f7b..b9166308 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_keystore.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_keystore.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800230500005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -d3b657142866a412fc24d2a779c80c321ea3e69dece2a96a317271be304e8da8 +9ef006a2e2a83939372f17729ae84a63e726b8f246e49b28f9c36c1132fa30f3 [key_blob] 0b27d6e7041cc18a06cd8e326d42265f960395b9b805ef826d6b0a61b46defbda8a30f91747a8a0f798ccea28b938cd7655378c5b80ff6d39212d1b389b053ad6398d74546e1b8520000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -7d620f6519eead40e4d53861c8f36ddafe03f31c3af7768aa583bd7114c1c2c15ec923cf48333ba30e46c86729d49d6affc0e31ff81a214e822d2d827c2d9728b52529d5dc1db2a72fb0e5d2de43865528998dab94b2869eb3969055f2f59b68d5dec53065c3989057dcdee6ab067489f76ef97872b520b6261dcfe18d7881bfaffc0b1b0837ead99136ce174d7db32809ac3f2ef8902a80d2c1e6d61f9f353fdf33da453bba3db5113fb3b5cdd401ce4e0d93b61f3c958c02db215deeeee9512c71607c7b68d7ba6f1e8ce96360621b9b34e41a250948b9ccad08170af0eb0099e0c5b3b604ec4ff16486547fd976f8178f52263fb674b8408865dc0c25ba8d +8f6f79ba0fa649eac7b7e5c6b58015ae3bc001345abb56ddd499e04380a558289133b1b2b66cdae23298b387a7ab26335610ef8f89655ebcea01ab4242ad69efe026a1e130c96f363c995138b75de2285f20408d2c7e0a424e00c0655f6bc7f7a561807c74c63998de1da71f8b0c85b0ba03267cf81aac40cbe572c50350c47f6c30947c188325c41056381ba6707c4383e45248ecc2585349766729c9f5ca904ee588b9ae87d1fafd292d5f201c43d621073362c6480c5773ab08b29e74e66682c39431af86f7d9f46034e14724b343a051cb692e3fdff2c7d963d4ce0c5e9633dab2836b1b4d870b6c1f2474bd81efc97e16da12af9e0e789db02741c34d3b [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ a701018000000000c604000001000000 48223c202223d60a35c00f8f2b3f847d99427339cc4b8c05e23654169bd49d0a [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_otp.sb.txt b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_otp.sb.txt index 20138777..962ee85e 100644 --- a/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_otp.sb.txt +++ b/tests/mcu_examples/data/rt5xx/debug_logs/app_xip_mcux_unsigned_otp.sb.txt @@ -2,13 +2,13 @@ [header] 000000000000000000000000000000000000000053544d5002010800240500005800000000000000d000000006000800050001007367746c00a0ff05634002000001000000000000000000000001000000000000000000000100000000000000 [hmac] -0e880c1ca42f51d56602be366d17ebb964f70bc33cbbb0f58d740d73e4f3bdd7 +1dfb10a1c681ccd4ac5aa8abaa07316982ace21f4869cce8ecd4261297930dfc [key_blob] 4a273c8f321743a44f3bf244ea1b8c30af9b93e30fe1fc7a2895240f14b16df4443c8a40303c89294fed1800170fdbf8da4bee1bf6b78c476a32ed27e53767df03e13fae4a5106800000000000000000 [cert_block] 63657274010000002000000000000000010000008004000001000000100300000c03000030820308308201f0a00302010202083cc30000babadeda300d06092a864886f70d01010b0500301b310b300906035504061302637a310c300a060355040313034d5720301e170d3231303131333130313933395a170d3431303130383130313933395a301b310b300906035504061302637a310c300a060355040313034d572030820122300d06092a864886f70d01010105000382010f003082010a0282010100cda8921013186f3834c339a51fb2bddb4f9376b3582af0834e3f2a07c01de78714c86290acc6baf2d86709e6927bc5ae167413979d735f427bedee9d19c7b78da92d0205e466b7f26f4f0e18c4e33aa7620a408ba31fcbdf6206d0ce4de5b9f11a8e6e8dd9a95b20bb3d73ad151bb3a9075e9300965b31cb93fd729681b21afca2ac45f1fe7d8b2b2212a8a85aa516e9a4cab4f733d27b65a5f7f05f0ae95ea66ab94cad926b1f1a0cc96f454def15d86c15dace0dee894cd68f02311a0627d5a6a41a56eb7fd866018206422e448aeaefb848757e3d91fd265af762798d3e8d7c8faf2bddb6ddd4944429a06ae424ab691b1af1003fc8322f97c7374df3aef50203010001a350304e30340603551d23042d302ba11fa41d301b310b300906035504061302637a310c300a060355040313034d572082083cc30000babadeda30090603551d1304023000300b0603551d0f0404030202f4300d06092a864886f70d01010b05000382010100ae9a1ce2112149399b98fd994f3fee3194bd61a41408f3ce6fbaa9475ad02b8d797154f7c6e5d057285696a86b6734c3cc8117bfe325dfad766de5940a021c50a290cddd571f6465a49300b4f29a52b025b4ac5f911587e6c484680b890eb4252377619e7f1e1d14beb6d45045059c5f34522f14d2ddafe4631c9e6e4b4873f4931bf6daea6c6b246d965c842de600c78a18d2c0b5c5e267c369027a53e6acfbfe12a51eda4790ba8411700655a663589c7840ad621364be35a12996efbe20fa94753e3ddc823f10aa65ca0e12b3dfb9b13b5e0c1cf3826f336bbda5263952321aba01e6240c5598a0c4a937ce989a8621ade87c7a0e8baf85f7437250e73156001c69b833f31937be8103acd3aa30ef7735a22121a5211590c13f8f5d838eac2cd1e11f3040862bdc6a4e87890d9c1cc7c74a5101273ae2bf56241dcf92329ba5f122943ed4523cf7409f68580d8804fed5f57c7ec300e7eb5fec5e80099ac35d046d9ac35dccbb79c50f863bd647fe99746a13535f80374bce40cae015ece7 [signature] -7ab3f47fa21e95f4560ddc800d5cb4e3255b19d821dea1033d3139727282346b2c669f722fcf8b793cfb1035b9ed0f00a58885beee5b9a4b053e45bd5fc8f364c098c3c74a9c1c24a1d93b0db46259b401d9ee792a9a858fc21961753a6e038647d718239a4216c294b51029243d947238cfb653a521254c2dc8ebb268db464432697e3c1b071548a200629a4eab0003cab45a92db77c84f356cbbce352822440146a27cf8836534edab233944b0bea25faa2c862b8c7e90012768d4b65b206cd3bd8d2469c76a21d12bdce67114f01d2cad6f6a6c94ae3fbe5eaf016784eb574f8664231bff93cec531585d8183d3bcc60bc7e6ff1046bf2ed6c6b21d36445c +887617083b282446afc9860251f8e9a414756a6c146f169a1785dc593b6a083115ad404fc569e8690e73c8e47f6ea454875237acd3a0b9b636147c44ff9f9d0ec3892bd4c8844b1ac0d61ee9b5fa078e911d5e4b348a595ff6bb330a158371ba2fec7c61bf7660afb61ee7cf8a9d236acacee001a06036400f7696587986c30f937e2f1775e74e0432f3ce8f2e0292383f35f61bf61f58613b76ce8e7be507988cc36c8f8f6335595ee1d92f700e5419d388accae5a87b5becb41dec85d67f318c81c221ec261cbba4df84f7f6e86e0f37ccfa692241d7c7507f461386d2fee7fbd56094b3d57843542632fd829e20937fb59132b13b9cab468c5dc4f9c672fe [boot_sections] [bootable_section] [header] @@ -19,7 +19,7 @@ a801018000000000c704000001000000 156a4ebdf118a9ffb8f7b1e927e707e9d81196ff649e2c67e1a06e5d0f76d88e [commands] [command:CmdFill] -6703000000c0100004000000063040c0 +6703000000c0100004000000c0403006 [command:CmdMemEnable] 4009000900c010000400000000000000 [command:CmdErase] diff --git a/tests/mcu_examples/data/rt5xx/output/app_ram_iar_crc_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_ram_iar_crc_keystore.sb index 294195730bc39fe3c996a86f8bd35ec0b6ec5349..05acb285f0775f5fcbc03909e9db354765f9bff6 100644 GIT binary patch delta 359 zcmV-t0hs=ve4u=gU?8zjBxSZafO$UKF0Qdq^&4uHYFwHH{Qz=6Uzl#4Fh`+)1b-L! z9_%rHApI*iR7dU|8jLooE-V}rdn-~`Iif?Y)(z+jjU~cPxWV=gAof!yhQD|J>JnYX z#bqa2u`vDaEqyWk$;{{ZKSXsBgHx*5@Yk0fabIHhZvKzsbP>OaZ9dc z3F<&Q3EKQ0&Y6NQ7?$_2#HHUM!SUz$DOJEMRHxpJ~;=5(dxniN7%cwU$zgbTD) z*Z7OPFCXm%3z;A`etbei(mFf?w$92)8vcY9N}(`>Q5*z|AlS{NE#MFMq5`2slYj*< zFu&5{Ltw&t)GvNdfg8^-_8A=X&#`Fb1yk1&9uq>&+}M|^I4?;JX*hdLH6u$-60_h1 FKscUHq+9?1 delta 359 zcmV-t0hs=ve4u=gU?7qGlC^2~AIbFb1RWt+-h`Pz>h1=+N}a!hk_T%Y-e94C1b>C5 zdE0HF*H7BuZST)y?F6|?K8(P6YcH=x-_2ciY8h=&CtA<1{cd$SSN5e+p3b4*}iQZ74DSjN9mq(nd zdzyojWy%zHtEWfa6oFWWRaFklJieNN2X02biK~ib;nXL-jfT&D$$1t0Q^{;tT*X5S zBR-8ji1@R-C*UpStp*&NY7bbOA?Bg)*i}wqq%H(Kb@W5ShFJ!>*1J8b?E;GTlYj*< zFuguWZ4vnOpuBJN)UXM;$tqi(0DX0deF1gW*s(9A%-EN!I4?;JX*hdLHRU@#)U)6P FKsXJ)vGD)^ diff --git a/tests/mcu_examples/data/rt5xx/output/app_ram_iar_crc_otp.sb b/tests/mcu_examples/data/rt5xx/output/app_ram_iar_crc_otp.sb index aa0d9f8c8e9715a0b5af24dd72dacd5e06ff1818..06662428180e0321c7f84718e83bb81e1b8b233a 100644 GIT binary patch delta 359 zcmV-t0hs=AW3ZDNle4J-p3cKE)bXj70o)r5!4MOTcHq@zM@ITeb+YyPB+-BEj2{1) zWipjW^9eQErG&<>?5+EJnlVWuDt`@N*IBV1w!Fhf`)lY6=ahukx%G}hher`jZ)q1O z*ri@Xad0QMdA0rjatYQ}3mBt>7~j4(WTOK*JYw?X72`MhDNZ66<5qSt3*1t0or-9U zIm6kqY{D{mDFv&Jfsq16CGBzwY6niqca(B+qn(ain15fL6F7!0`!}~0P6DuDlYj*< zFx;DwI%2Gn@QWBFz*epawG%B?UO6XuOBgnHww|Nz0N9tSI4?;JX*hdLH6u$-60_h1 FP&g-Ssek|g delta 359 zcmV-t0hs=)W5h~t=-1=%{3uK~WhD#SQmrKX{P1b+Zz zZLho=e}<%VMn=h8kTf|>*<^1EUGFoox7{tATAt9`cM=mJyOFY1^k=j)Oq&NcC~xaF z-m@?M9SuHmjGeYreQRFW@XUhF=qk+yBvy zcsZN5SEZA?uAHhO`aTlA#>YYfM^-5e3P@(2ijO^sfc9|9xdwqgo#;c6QUY9aM~_?zkoppm(Atx;Lz^{OuH6>&7*VYX(ho)@?pDzyjTjlYj*< zFnB`1zv3yJ(>~&09J;tW0#Vv)J`XT*Wc{2L`%5$ADA<>)I4?;JX*hdLHRU@#)U)6P FP&j_1t-$~Q diff --git a/tests/mcu_examples/data/rt5xx/output/app_ram_iar_encr_keystore_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_ram_iar_encr_keystore_keystore.sb index ec9dafa6469655de0ff5ae309fbb0d0b770bf741..68127b0f2a047fd6aa6b7bcbd57e8436412bbbdc 100644 GIT binary patch delta 361 zcmV-v0ha#Ikpa+=0gzxIF@7boR9$vI@S%JoG_Gu$o(-g1Dorvo?y*zCnjQw!p@0N` z8HZ*Yv!8mxr`cn7Y!+LxOT-Mw;bIC-e8Wb`GVYGcjL2Eom?2YW7L&dq!RnGyf9Ot;i)?nKkHdm| znKRv2yDmo@(AXH&@*i!fs&q?Cv!U335Y3TdnJ%JXV6EZAp+WwZ0`q{gG}U`Aqc%BL zO^3=ikF=Mm52dk>5Fc_>#2pgDI_fB;N00P)fM^85yfech%4}Lw3O4>m^t!j12F7bw zNDEZqWH_!Y5~x}85HumPV-cZumn6CgQxAR4%b$uFl9~L(Qj$)3j}()9X_IVPo`G@?&-J7%}Yq4lp>~{H$CgW)m^Zp@0N` z$Vu@pa>6<H7h zHr)49>p4Gfg@Knr%+Gbd751SjnwdU-y_#;gGU#2d5`TvM;@;REn;|@df#p@jGGbgy z#I#xMZ=aB~C?D7i%o(l`|3d9er^9xp_0*F`s)oWJE|W2njmk1`fJJ*s-*Ih$0F#$Q z`s*{|x*;gd^o#z`@W)LG{U)#vb5{)mklTl*Q(FH)=mLB^AS z1u!rc@IsYGRs%)&0YJMcA8;|(D`C0+m9}(e7g9OaIN0Ram#a80NeyW@drdXvJ3iF2 H;05qPS%l_RI~=GK}Jr+O`^t&kiR{9qtLep@0N` zxXM>FbP7K2a40ok+vO+FBvKl6jo6P&goqRKb-o2=dwWZXGn||xV>Lm~v7$Fvg{Xj= z2~vv=6wr@R-5&@@sL64|mtVW-7{dAm5|MJ-nz?@>_LwX$(338dsDGWpbx{{DiJMVN zb!zSv#mjR-m^QTVP$vRrG{2-dQ)pR#=3mPl9P`XOrM+fTfJMNmiqZsZVs?YslJsJD zHx4m?m_u_$IxOqA0=ENrn^$oN{dfNvh~GD7J3fZQy(|9;ZYXO*-vbRuyUJxA4sAR1 ze=;`Z^Bw81dv6zLY!l6ZW}ZQ@@7+{d#HduiKb0AytUR{lE#8lw=;#hh*HDB4q~()< z1u!sLI0PK=8Wu_@*)i~o@ObWbnbbaLP9okkMJQdvpXS_T(gfmEz_fBt@B}Di0PP0h$_ezmMq%qX2m3-^o8uJD&VtcwU z9OK45+!2P#%YakXx;CZvV8lOjc&?xfHt`SFXYNnwfvhQVGs9oY{6!| zQvioW(R+1H22XGjqtPTW+t#De*%D!YA`%ysSt=-N{4i^MMf~74#~?En=>&4*Np^bw zpkCAe!=gi#`NrK51DOh;SxS`=$lOMj*xtbquFKIyUvYZ>_LiN#oT&{@$`{HG_&lBT z`|-wV2kuDuWxUjW|8*h9(jELll@>H@e0FGqe@lEMz$9B(tJC2JQ>ygrf<~4Cd$p5* z1u!s7Qukx}gi?}ypk%FEqQ(B%VTYD}ua#WlROIWVQo(!Jm#a80NeyW@drdXvJ3iF2 H;02&UD4(=j diff --git a/tests/mcu_examples/data/rt5xx/output/app_ram_iar_unsigned_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_ram_iar_unsigned_keystore.sb index 6995795fcc1756f005927ba12422084068d16fad..afde3826d086e6350432b04c9a87757fd579ab05 100644 GIT binary patch delta 359 zcmV-t0hs=ve4u=gU?3M3V0;yp<2n0_{&m&L3)$);xfj*8Rx+gAkZ$DZ$+e+?1b=6Z z9+9XTbGUf9NbDLF527AKx$m=zN%I|J7Vg3m&kSWNIN%YMf$IwNEyraF&@#ntvBkC61?4N(NC*%z) zQ=)%=tn;oys5y}0)-My0IxqRUx`Jh?(d?ViZw1l9amoET+V&u&)35e!OCfup+8&&N zbn9H#nps|IycwuSFEdW8uKtI1X<)uPa2)O`Csoj)4Z&^FPHCbaF8hJdoC4TrlYj*< zFg>gklZbcLI|_vn{+gC%UJ*|n=ZN-Q=l%j%uu8OZZ`hZsI4?;JX*hdLH6u$-60_h1 FKsZbAvvmLf delta 359 zcmV-t0hs=ve4u=gU?5`+QIwSB;_{C3m!GmzwgfZgZ8*#EKl`18A;m#g*F2$s1b-}2 zl=j-LyLtn$r39io&~^<3up|FS>X9W+FSt1+zMULim_kuDkWbC!?MMlxAxPc)V8`HB z&cJ4GrV0V{SGNzN2lZUaTBE#RSq1SnN;|q6NU&jpcwQEY_?V%)@}}j9NILC9It%65 z-qo`ib}CC8psmK4Xm7*ws`)Lqz@t7{gPVKx#^U zQWRvF>N!gcCsS959Zl8RB2>q*QupTQB}(d~3aw(0)7PuP6R`&_SNm9|7G};a2Dw%Y z9lX9=plADFfxHcG=IrtU@_oMUVMe#{RBOZd?(7=(xc>UtSQ-WxwzgkzKLV9klYj*< zFq6_H(EC8jf`;R**r@wkKNMIpdyt+K;D7%)P`+`u1lX6WI4?;JX*hdLHRU@#)U)6P FKsb8(vD*Lu diff --git a/tests/mcu_examples/data/rt5xx/output/app_ram_iar_unsigned_otfad_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_ram_iar_unsigned_otfad_keystore.sb index 6bec90a161c7b204ea5a901102d308b338bfaea0..9f7a29a7abfd4518b46327eb16360beacca5fb46 100644 GIT binary patch delta 361 zcmV-v0ha#Ii2=}w0gzxIFy!-~KcW>{aMtwV5jwwJigZTyd^j1ZHrXf0HyFI5p@0N` z4h$M$Z`KJhAe+&Z0VYi46whTtBNrtF)#$zDE%rCF9`_gQUxJ0N*tCLs>M$ik2HEO!=6bZBM{|?f9}B{qWfyX9&6|O}h0D1YTy9BduDqLwrMb z%bL%pXV(L_;lXL_hm)6jGAN2WruVC^y4v#(RqzitS7F`dyL0K6Y3`>>kbwy%UYKas ze(MH!=fjZ_+ZTh@mO}%mBB|gLXE&$Si;k2k!mY4L6n=;||AW!qSL>@X{dLdjM90hq zJS=4WLaWM3@ah|0{~ros39* zOqhJ>dZSk)HCug{h(-_-SwjVjHo*jTch7W`NeRvkfPD%{;vb@GjYD zw|IZXrA@F!8s2p$t&vH6 zKvCJL2PD0_U?U~CW0f#o!UU9v8v=I{6>MKTZ!s9K(x#XMJ5GE;D0#BRrzQ^qiA0lt z1u!rI?=cYZ!C`CI`F3DNU=%R2vc1CkIUcKm#a80NeyW@drdVXOHLBA H;05qL`~tA} delta 361 zcmV-v0ha#Ii2=}w0gzxIS(hqq?z2(4O5~&tTp@0N` zj~ea2$}Q*<8~M3oG@I?emW}V8k>Mk`U#aolm~sKOoI3oVZ-5pQFYUv&GJIQzO*`aA zYtT_?w{VCyLIMy!++^Aq{y=-Hzwsat)(*Xm*&yH0zYb6b=E|)|VLaCU=3@$igif3P ztQuHM>?1cQuBY(4Va5U?{s5@yAjlhkK1%wU0z|Yo(|H1DH@L54_)ZS4Scg)xLt<1Q zOBn+KZmLdvPRRs72-*k+7|gG%zjU|V``+1l^+%TKjVLCv9NBEs!m@R_@?U&$lOfrs z1++*8`=CWnwbB~ZB+d6H{6Z%Ta2a{;c&5G^yK2;lCyb({)ZyVHB0{$>>O<=Ssp6A> z1u!teGY`iObV$^ko4D5S4hc>e<9`koiuD37wxL1jqc*0w+nKq$-1b@mK z%jqh-;L?sPiPnoCuB@NLrGSA6zWvStgr7qC+J)XDr;4|;7~dfxcs}^-ep%q);8N`_ z>=(O1giEdnL_}$0LPg`2#g&0c^5RF#Oi-oJKH={Rk>wsaEiw%&lDF+n3eCicWr9rrl0yR8}_P{!r^36ECZ(aDAf2 zyw6GQIscY)%e}ub9iUQ4alYj*< zFb%_LE>DG!5z=Ff=rvn=pI0`MoQ%hy{r+-8dwGezZ`hZsI4?;JX*hdLH6u$-60_h1 FP&n)au?7GD delta 359 zcmV-t0hs==`aI!r)k2OgZP4@~AKoOU=27a!S*A!REzpeM9gTpjk1T zp%(z!*dtAdaK!->HH-3dBF!S*`hW8se3Y4Bp-?F)8bs!addd9_(HMAf7iQIcP2gq+ac>1dZPX34g@h>VS6n_E<uj5G2s9xr@U?eE3D#*l4AECU=9&U zV=HxbS9;ZtgPGGaB8{znh$Tv+`}m)Ma^OzmuzmoA_Vd;scS)1cS$F-Z4+0{AlYj*< zFto&VnQKjV`6rm*WQ%|Iob?wORR$F9%iAp;RmG5ome`l8I4?;JX*hdLHRU@#)U)6P FP&kt)tU>?) diff --git a/tests/mcu_examples/data/rt5xx/output/app_xip_iar_signed_otfad_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_xip_iar_signed_otfad_keystore.sb index 84875b2694e1da8e1dd1658477689d2dba8b2cdb..20f36fe50b69ea9898874f52e2874ebdf9e98c88 100644 GIT binary patch delta 361 zcmV-v0ha#Il>yL|0gzxIjX7*fl!b95r@928J462%Yu$gbihaj98m3p@0N` ztjKo_Wc|72HMq~h^m1Qs7x67m0JZACY>u2fbbWcWsTdK8uUbF-Vkdo=xH)gVusN14*hR`~bIV zsjZyLaojT8p_MtuKNb1_qjO;oazU#&Dat}O_{)$c5z%7cC&zkdEMA1RJ24#suECRl z1u!t#Nyoy7RbzbfQmTB?AxJ{iZ4oqKS|0gjF2#=T04)UAm#a80NeyW@drdVXOHLBA H;05qR>`kqF delta 361 zcmV-v0ha#Il>yL|0gzxI2on96-knl@`9LekSi&hBNDb_lfbmx;^;1|oT{M?Ep@0N` z1_19bgsR`2w>7jmI=q)ZI^ovm45M-?X5D}4vp(Ymik zB?hPGPXN%SJj_R|1HM-$v1A2u90hro1Wm#Yck8)23a;8no+XBV?NyzcS-vX!x+R%=@(1nB_4b`fWR~)`vxqx# zTrNLLFnR3TJ(+m=r1muN^7M0DM?AXba&x#Jcuw2yb}+vTaX4f^gy`?UjoG(Wn&t*8 z;Sug*qfGdYg2naq6ief<@G_%)7{WojwvWZ{|0}_@7>o=;XVrtNF8UqPXi)M3$cmGI z1u!sjtE1g!6;nm(*4_dOna=TFXyL|0gzxIZBS?vzF^l$$sd;OH)eEVIxMxqIfp2PuOOrA7_W@zp@0N` zZu^U$Y|=ydA5=+jXuzeL>DdnnkxYns2S)l20Yc4C6{8Y=nl)Hc0?v7_F#c}v#@Gwz_4y-QACWPHVIH1(1W=60i-Wv#)8!}8yO zrz--k)vR<$`TJ+les48AW>m6DdlBOsD4pvMHT8;0GO*}4OH^kCF=zc;-L~z}yL|0gzxIt9p(QVZLlttDzukW@)5#qHH!5pKF>OWq1QizD%2Ip@0N` zcf8JU+xaeC1dG2>xib78nUq+2Hb}sw4Gz&9=K8KQg!vjRMXApL-Q_W_=!Gv^XHP(q zo*%Xe_F}cE z;7duMUW|T~h%N+uY)9Lz643F@RkhZ-u$om#a80NeyW@drdXvJ3iF2 H;05qRtJJN5 diff --git a/tests/mcu_examples/data/rt5xx/output/app_xip_mcux_unsigned_keystore.sb b/tests/mcu_examples/data/rt5xx/output/app_xip_mcux_unsigned_keystore.sb index a01673a3ee7ee224caf4e9b28e429ae1e88e1f87..f901031535f5d5e3fc9b5f7526b764b754d125b8 100644 GIT binary patch delta 361 zcmV-v0ha!-qyey`0gzxIp6~{u;;1<}H!l}*n&?Vn=O(!FM&z3)`NM1xGWsy{p@0N` zk8gRp52i`#$G7FiwSX0_JHP=nTDw-=)S2KzfTdU{ku$NfwrtwsGMKZ6r>iD2RuJ!x ziDh2A>H(`lLak}<;3lErFv)K=Jeg5Aw_V~WUm!q@EPe_?P5{7VUu(zrrD1@4bjCTD z-W{hOiwuRZx&tPB_!_K0%jIm_&e{3+6d>Dfz#1K|E8>Vo4Lxbc}NbJH`Q%QDb zDarN9kWS@@xvq!N`u!;_UmzSq)*%NoV#Y`eS97ZfvYvG2W`e_%F|UUA+4NvE;YTF1 zL!eR1X)ZtC^2ga@)XofEmNVM2gKHa2hYM^UBy_!j@5z1^+7hpx4tSlgCqcskO*@l- z1u!sj=&|a7^CXf4)KP1z8NmA2=(tH?0+_9sQr7AhD+1xzm#a80NeyW@drdVXOHLBA H-~~`jqvNdp delta 361 zcmV-v0ha!-qyey`0gzxI)3#R>C}yM*{3Oz+dB_Yh9;4=+?Bc0vF>-OfFiwrAp@0N` zePRz~8Sbq>0@AG z8=Exb8YKxxxy-Ey7Ygv}0GZ&$v$h26Pw`}iRDaoa_!o~-CO@`xxIlFDM20LayGk&E8Kw_w89m#a80NeyW@drdXvJ3iF2 H-~~`j8X2=D diff --git a/tests/mcu_examples/data/rt5xx/output/app_xip_mcux_unsigned_otp.sb b/tests/mcu_examples/data/rt5xx/output/app_xip_mcux_unsigned_otp.sb index 3bbf6774d8d16ed5bffc903d823587c9e9d43913..3ca673c89443cdeb71939d4625865b8b39bcb310 100644 GIT binary patch delta 361 zcmV-v0ha#2qyfOB0gzxI9s3ZW#(~V#tXin6ss}M?f~?{nNNLRI?9?U_my-?rp@0N` zh;|nUJ18VZugQi2QTXYk6m@EB6mJ%q7lqteJ8B3q6|F!|#cAki4s*!le{Q5yhf+7J z)1bMwHWYkB|DT->!-*@@$b?H8z}6n=wfYB+ksV%3G>TbY_Pa9*6@ziQFYJ6_zjk1+ zwjSrtik%~B%Ff^cpkOvY4|bMVd4|J(50ic`7j@@O1Tyo^k1hg|I6pP^8}=VqVLNut zj(g!#EX(B<_$54Mp6Nb|M=lj)Ql(W@%LsTX*{eqq#lYh05GO;_Ht454n#QDYoa{iNm z1u!sC^3}>_4X#0ZyE8h*?o!U`Dk=6+C$qie!Z%1neRt?;M4P3P2C0iNTA>N?_Jux|Qa)LB#EM}i_FVBm4Jo^wex$O@CrHF;T z?pvBm1wKW+U&!-hz?j3wN}L=dq1ihPv|?Gb0om?(Dw>6l!Wm(8I&K4oN7oo5nnD)B zl(i5kBt4XJIM23Ir6DCuEy(M#Xxm1AL^5f9JR1iUNTL8@nog?#1In~olG}I4Pc>}2 z&NV0^L;*&keE5T9H0`S+IYh9&qF<^khAWJIkO3!X)V5n7Y}37sBx%QLA<-+`=5ZA8 z9W1SHYHXCQKfYeC0cV8kS5JmyBOCvd&c!iUU4etsyv7U1=Kl~zzb@9svK=-8L|l`A z1u!sSM);(TDxriJ6!LvYpp^P3paUaBNU;nD;vl~l*%lz!m#a80NeyW@drdXvJ3iF2 H;00hzKklM3 diff --git a/tests/mcu_examples/test_rt10xx.py b/tests/mcu_examples/test_rt10xx.py index 23527611..14d91d38 100644 --- a/tests/mcu_examples/test_rt10xx.py +++ b/tests/mcu_examples/test_rt10xx.py @@ -28,7 +28,6 @@ from spsdk.sdp import SDP, ResponseValue, StatusCode, SdpCommandError from spsdk.sdp import scan_usb as sdp_scan_usb from spsdk.utils.easy_enum import Enum -from enum import Enum as PyEnum from spsdk.utils.misc import load_binary, DebugInfo, align, align_block from tests.misc import compare_bin_files, write_dbg_log @@ -852,13 +851,13 @@ def test_sb(cpu_params: CpuParams) -> None: sb = SecureBootV1(version=SB_FILE_VERSION, timestamp=timestamp) sect = BootSectionV1(0, SecureBootFlagsV1.ROM_SECTION_BOOTABLE) # load 0xc0233007 > 0x2000; - sect.append(CmdFill(INT_RAM_ADDR_DATA, pack(" 0x3000; - sect.append(CmdFill(INT_RAM_ADDR_DATA, pack(" kAbsAddr_Ivt; @@ -889,7 +888,7 @@ def test_sb_multiple_sections(cpu_params: CpuParams) -> None: sb = SecureBootV1(version=SB_FILE_VERSION, timestamp=timestamp) sect = BootSectionV1(0, SecureBootFlagsV1.ROM_SECTION_BOOTABLE) # load 0xc0233007 > 0x2000; - sect.append(CmdFill(INT_RAM_ADDR_DATA, pack(" None: # For example this fails on EVK-RT1060: sect.append(CmdErase(EXT_FLASH_ADDR, 0x100000)) sect.append(CmdErase(EXT_FLASH_ADDR, 0x10000)) # load 0xf000000f > 0x3000; - sect.append(CmdFill(INT_RAM_ADDR_DATA, pack(" kAbsAddr_Ivt; diff --git a/tests/mcu_examples/test_rt5xx.py b/tests/mcu_examples/test_rt5xx.py index ec39ce51..bc13b42d 100644 --- a/tests/mcu_examples/test_rt5xx.py +++ b/tests/mcu_examples/test_rt5xx.py @@ -650,7 +650,7 @@ def test_sb_unsigned_keystore(data_dir: str, subdir: str, image_name: str) -> No # create boot section 0 boot_section = BootSectionV2( 0, - CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), + CmdFill(address=0x10C000, pattern=int('063040C0', 16)), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), CmdErase(address=0x8000000, length=0x10000), CmdLoad(address=0x8000400, data=fcb_data), @@ -720,7 +720,7 @@ def test_sb_unsigned_otp(data_dir: str, subdir: str, image_name: str) -> None: # create boot section 0 boot_section = BootSectionV2( 0, - CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), + CmdFill(address=0x10C000, pattern=int('063040C0', 16)), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), CmdErase(address=0x8000000, length=0x00800), CmdErase(address=0x8001000, length=0x10000), @@ -784,7 +784,7 @@ def test_sb_signed_encr_keystore(data_dir: str, subdir: str, image_name: str) -> # create boot section 0 boot_section = BootSectionV2( 0, - CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), + CmdFill(address=0x10C000, pattern=int('063040C0', 16)), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), CmdErase(address=0x8000000, length=0x10000), CmdLoad(address=0x8000400, data=fcb_data), @@ -871,7 +871,7 @@ def test_sb_otfad_keystore(data_dir: str, subdir: str, image_name: str, secure: boot_section = BootSectionV2( 0, # configure external FLASH - CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), + CmdFill(address=0x10C000, pattern=int('063040C0', 16)), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), # erase the FLASH CmdErase(address=0x8000000, length=0x10000), @@ -983,7 +983,7 @@ def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) boot_section = BootSectionV2( 0, # configure external FLASH - CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), + CmdFill(address=0x10C000, pattern=int('063040C0', 16)), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), # erase the FLASH CmdErase(address=0x8000000, length=0x10000), diff --git a/tests/pfr/data/CMPA_96MHz.bin b/tests/pfr/data/CMPA_96MHz.bin index c11722c262ac7da0776419dfb647889c55375913..6945453d4e736789a3dd0be73fad76b5f7194dbf 100644 GIT binary patch delta 44 kcmZo*X<$)kU_b@`nHc`+GhO%#5_JHgiG>Cm12!-M0HSya2><{9 delta 44 kcmZo*X<$)kU_b@`nHm1*GhO%#5_JHgiG>Cm12!-M0HS&c2><{9 diff --git a/tests/pfr/data/bad_dev.yml b/tests/pfr/data/bad_dev.yml new file mode 100644 index 00000000..a3a0552b --- /dev/null +++ b/tests/pfr/data/bad_dev.yml @@ -0,0 +1,13 @@ +# NXP LPC55S6X PFR CMPA configuration +description: # The PFR CMPA configuration description. + device: invalid # The NXP device name. + revision: a1 # The NXP device revision. + type: CMPA # The PFR type (CMPA, CFPA). + version: 0.3.1 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR CMPA registers configuration. + REG: +# Reg Description: Test Register + name: REG # The name of the register + value: '0x00000000' # The value of the register diff --git a/tests/pfr/data/bad_rev.yml b/tests/pfr/data/bad_rev.yml new file mode 100644 index 00000000..b937b229 --- /dev/null +++ b/tests/pfr/data/bad_rev.yml @@ -0,0 +1,13 @@ +# NXP LPC55S6X PFR CMPA configuration +description: # The PFR CMPA configuration description. + device: lpc55s6x # The NXP device name. + revision: invalid # The NXP device revision. + type: CMPA # The PFR type (CMPA, CFPA). + version: 0.3.1 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR CMPA registers configuration. + REG: +# Reg Description: Test Register + name: REG # The name of the register + value: '0x00000000' # The value of the register diff --git a/tests/pfr/data/cfpa_pfrc.json b/tests/pfr/data/cfpa_pfrc.json index 7f89dafa..2a941192 100644 --- a/tests/pfr/data/cfpa_pfrc.json +++ b/tests/pfr/data/cfpa_pfrc.json @@ -26,8 +26,7 @@ "TAPEN": "0b0", "CPU1_DBGEN": "0b0", "ISP_CMD_EN": "0b0", - "FA_CMD_EN": "0b0", - "ME_CMD_EN": "0b0", + "PMCMD_EN": "0b0", "CPU1_NIDEN": "0b0", "UUID_CHECK": "0b0", "INVERSE_VALUE": "0x0000" @@ -40,8 +39,7 @@ "TAPEN": "0b0", "CPU1_DBGEN": "0b0", "ISP_CMD_EN": "0b0", - "FA_CMD_EN": "0b0", - "ME_CMD_EN": "0b0", + "PMCMD_EN": "0b0", "CPU1_NIDEN": "0b0", "INVERSE_VALUE": "0x0000" }, diff --git a/tests/pfr/data/cfpa_test.yml b/tests/pfr/data/cfpa_test.yml new file mode 100644 index 00000000..8b769704 --- /dev/null +++ b/tests/pfr/data/cfpa_test.yml @@ -0,0 +1,25 @@ +# NXP LPC55S6X PFR spsdk.pfr.pfr configuration +description: # The PFR spsdk.pfr.pfr configuration description. + device: lpc55s6x # The NXP device name. + revision: a1 # The NXP device revision. + type: CFPA # The PFR type (CMPA, CFPA). + version: 1.3.0 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR spsdk.pfr.pfr registers configuration. + VERSION: +# Reg Description: . + name: VERSION # The name of the register + value: '0x00000002' # The value of the register + ROTKH_REVOKE: +# Reg Description: . + name: ROTKH_REVOKE # The name of the register + bitfields: + RoTK0_EN: 1 # The width: 2 bits + RoTK1_EN: 1 # The width: 2 bits + RoTK2_EN: 1 # The width: 2 bits + RoTK3_EN: 1 # The width: 2 bits + CMPA_PROG_IN_PROGRESS: +# Reg Description: CMPA Page programming on going. This field shall be set to 0x5CC55AA5 in the active CFPA page each time CMPA page programming is going on. It shall always be set to 0x00000000 in the CFPA scratch area. + name: CMPA_PROG_IN_PROGRESS # The name of the register + value: '0x5cc55aa5' # The value of the register diff --git a/tests/pfr/data/cmpa_96mhz.json b/tests/pfr/data/cmpa_96mhz.json index ca83ce94..4185111f 100644 --- a/tests/pfr/data/cmpa_96mhz.json +++ b/tests/pfr/data/cmpa_96mhz.json @@ -14,8 +14,7 @@ "TAPEN": "0b1", "CPU1_DBGEN": "0b1", "ISP_CMD_EN": "0b1", - "FA_CMD_EN": "0b1", - "ME_CMD_EN": "0b1", + "PMCMD_EN": "0b1", "CPU1_NIDEN": "0b1" }, "CC_SOCU_DFLT": { @@ -29,6 +28,7 @@ "SECURE_BOOT_CFG": { "SKIP_DICE": "0b01", "SEC_BOOT_EN": "0b01" - } + }, + "ROTKH": "0x0000000000000000000000000000000000000000000000000000000000000000" } } \ No newline at end of file diff --git a/tests/pfr/data/cmpa_96mhz.yml b/tests/pfr/data/cmpa_96mhz.yml new file mode 100644 index 00000000..c2d3d360 --- /dev/null +++ b/tests/pfr/data/cmpa_96mhz.yml @@ -0,0 +1,87 @@ +# NXP LPC55S6X PFR CMPA configuration +description: # The PFR CMPA configuration description. + device: lpc55s6x # The NXP device name. + revision: a1 # The NXP device revision. + type: CMPA # The PFR type (CMPA, CFPA). + version: 1.3.0 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR CMPA registers configuration. + BOOT_CFG: + bitfields: # The register bitfields + BOOT_SPEED: BOOT_CFG_BOOT_SPEED_VALUE_1 # Width: 2b[0-3], Description: Core clock: + # Possible Enumeration values: + # - BOOT_CFG_BOOT_SPEED_VALUE_0: Defined by NMPA.SYSTEM_SPEED_CODE + # - BOOT_CFG_BOOT_SPEED_VALUE_1: 96MHz FRO + # - BOOT_CFG_BOOT_SPEED_VALUE_2: 48MHz FRO + CC_SOCU_PIN: + bitfields: # The register bitfields + NIDEN: CC_SOCU_PIN_NIDEN_VALUE_1 # Width: 1b[0-1], Description: Non Secure non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_NIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_NIDEN_VALUE_1: Fixed state + DBGEN: CC_SOCU_PIN_DBGEN_VALUE_1 # Width: 1b[0-1], Description: Non Secure debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_DBGEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_DBGEN_VALUE_1: Fixed state + SPNIDEN: CC_SOCU_PIN_SPNIDEN_VALUE_1 # Width: 1b[0-1], Description: Secure non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_SPNIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_SPNIDEN_VALUE_1: Fixed state + SPIDEN: CC_SOCU_PIN_SPIDEN_VALUE_1 # Width: 1b[0-1], Description: Secure invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_SPIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_SPIDEN_VALUE_1: Fixed state + TAPEN: CC_SOCU_PIN_TAPEN_VALUE_1 # Width: 1b[0-1], Description: JTAG TAP enable + # Possible Enumeration values: + # - CC_SOCU_PIN_TAPEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_TAPEN_VALUE_1: Fixed state + CPU1_DBGEN: CC_SOCU_PIN_CPU1_DBGEN_VALUE_1 # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_CPU1_DBGEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_CPU1_DBGEN_VALUE_1: Fixed state + ISP_CMD_EN: CC_SOCU_PIN_ISP_CMD_EN_VALUE_1 # Width: 1b[0-1], Description: ISP Boot Command enable + # Possible Enumeration values: + # - CC_SOCU_PIN_ISP_CMD_EN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_ISP_CMD_EN_VALUE_1: Fixed state + PMCMD_EN: CC_SOCU_PIN_PMCMD_EN_VALUE_1 # Width: 1b[0-1], Description: Fault Analysis/Mass Erase command enable + # Possible Enumeration values: + # - CC_SOCU_PIN_PMCMD_EN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_PMCMD_EN_VALUE_1: Fixed state + CPU1_NIDEN: CC_SOCU_PIN_CPU1_NIDEN_VALUE_1 # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_CPU1_NIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_CPU1_NIDEN_VALUE_1: Fixed state + CC_SOCU_DFLT: + bitfields: # The register bitfields + NIDEN: CC_SOCU_DFLT_NIDEN_ENABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_NIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_NIDEN_ENABLE: Enable + DBGEN: CC_SOCU_DFLT_DBGEN_ENABLE # Width: 1b[0-1], Description: Non Secure debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_DBGEN_DISABLE: Disable + # - CC_SOCU_DFLT_DBGEN_ENABLE: Enable + SPNIDEN: CC_SOCU_DFLT_SPNIDEN_ENABLE # Width: 1b[0-1], Description: Secure non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_SPNIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_SPNIDEN_ENABLE: Enable + SPIDEN: CC_SOCU_DFLT_SPIDEN_ENABLE # Width: 1b[0-1], Description: Secure invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_SPIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_SPIDEN_ENABLE: Enable + CPU1_DBGEN: CC_SOCU_DFLT_CPU1_DBGEN_ENABLE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_CPU1_DBGEN_DISABLE: Disable + # - CC_SOCU_DFLT_CPU1_DBGEN_ENABLE: Enable + CPU1_NIDEN: CC_SOCU_DFLT_CPU1_NIDEN_ENABLE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_CPU1_NIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_CPU1_NIDEN_ENABLE: Enable + SECURE_BOOT_CFG: + bitfields: # The register bitfields + SKIP_DICE: 1 # Width: 2b[0-3], Description: Skip DICE computation. 00 - Enable DICE 01,10,11 - Disable DICE + SEC_BOOT_EN: 1 # Width: 2b[0-3], Description: Secure boot enable. 00 - Plain image (internal flash with or without CRC) 01, 10, 11 - Boot signed images. (internal flash, RSA signed) + ROTKH: + # Reg Description: ROTKH0 for Root of Trust Keys Table hash[255:224] ROTKH1 for Root of Trust Keys Table hash[223:192] ROTKH2 for Root of Trust Keys Table hash[191:160] ROTKH3 for Root of Trust Keys Table hash[159:128] ROTKH4 for Root of Trust Keys Table hash[127:96] ROTKH5 for Root of Trust Keys Table hash[95:64] ROTKH6 for Root of Trust Keys Table hash[63:32] ROTKH7 for Root of Trust Keys Table hash[31:0] + value: 0000000000000000000000000000000000000000000000000000000000000000 # The value width: 256b diff --git a/tests/pfr/data/cmpa_96mhz_rotkh.yml b/tests/pfr/data/cmpa_96mhz_rotkh.yml new file mode 100644 index 00000000..36c2b3bd --- /dev/null +++ b/tests/pfr/data/cmpa_96mhz_rotkh.yml @@ -0,0 +1,87 @@ +# NXP LPC55S6X PFR CMPA configuration +description: # The PFR CMPA configuration description. + device: lpc55s6x # The NXP device name. + revision: a1 # The NXP device revision. + type: CMPA # The PFR type (CMPA, CFPA). + version: 1.3.1 # The SPSDK tool version. + author: NXP # The author of the configuration. + release: alpha # The SPSDK release. +settings: # The PFR CMPA registers configuration. + BOOT_CFG: + bitfields: # The register bitfields + BOOT_SPEED: BOOT_CFG_BOOT_SPEED_VALUE_1 # Width: 2b[0-3], Description: Core clock: + # Possible Enumeration values: + # - BOOT_CFG_BOOT_SPEED_VALUE_0: Defined by NMPA.SYSTEM_SPEED_CODE + # - BOOT_CFG_BOOT_SPEED_VALUE_1: 96MHz FRO + # - BOOT_CFG_BOOT_SPEED_VALUE_2: 48MHz FRO + CC_SOCU_PIN: + bitfields: # The register bitfields + NIDEN: CC_SOCU_PIN_NIDEN_VALUE_1 # Width: 1b[0-1], Description: Non Secure non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_NIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_NIDEN_VALUE_1: Fixed state + DBGEN: CC_SOCU_PIN_DBGEN_VALUE_1 # Width: 1b[0-1], Description: Non Secure debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_DBGEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_DBGEN_VALUE_1: Fixed state + SPNIDEN: CC_SOCU_PIN_SPNIDEN_VALUE_1 # Width: 1b[0-1], Description: Secure non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_SPNIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_SPNIDEN_VALUE_1: Fixed state + SPIDEN: CC_SOCU_PIN_SPIDEN_VALUE_1 # Width: 1b[0-1], Description: Secure invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_SPIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_SPIDEN_VALUE_1: Fixed state + TAPEN: CC_SOCU_PIN_TAPEN_VALUE_1 # Width: 1b[0-1], Description: JTAG TAP enable + # Possible Enumeration values: + # - CC_SOCU_PIN_TAPEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_TAPEN_VALUE_1: Fixed state + CPU1_DBGEN: CC_SOCU_PIN_CPU1_DBGEN_VALUE_1 # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_CPU1_DBGEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_CPU1_DBGEN_VALUE_1: Fixed state + ISP_CMD_EN: CC_SOCU_PIN_ISP_CMD_EN_VALUE_1 # Width: 1b[0-1], Description: ISP Boot Command enable + # Possible Enumeration values: + # - CC_SOCU_PIN_ISP_CMD_EN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_ISP_CMD_EN_VALUE_1: Fixed state + PMCMD_EN: CC_SOCU_PIN_PMCMD_EN_VALUE_1 # Width: 1b[0-1], Description: Fault Analysis/Mass Erase command enable + # Possible Enumeration values: + # - CC_SOCU_PIN_PMCMD_EN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_PMCMD_EN_VALUE_1: Fixed state + CPU1_NIDEN: CC_SOCU_PIN_CPU1_NIDEN_VALUE_1 # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug enable + # Possible Enumeration values: + # - CC_SOCU_PIN_CPU1_NIDEN_VALUE_0: Use DAP to enable + # - CC_SOCU_PIN_CPU1_NIDEN_VALUE_1: Fixed state + CC_SOCU_DFLT: + bitfields: # The register bitfields + NIDEN: CC_SOCU_DFLT_NIDEN_ENABLE # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_NIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_NIDEN_ENABLE: Enable + DBGEN: CC_SOCU_DFLT_DBGEN_ENABLE # Width: 1b[0-1], Description: Non Secure debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_DBGEN_DISABLE: Disable + # - CC_SOCU_DFLT_DBGEN_ENABLE: Enable + SPNIDEN: CC_SOCU_DFLT_SPNIDEN_ENABLE # Width: 1b[0-1], Description: Secure non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_SPNIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_SPNIDEN_ENABLE: Enable + SPIDEN: CC_SOCU_DFLT_SPIDEN_ENABLE # Width: 1b[0-1], Description: Secure invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_SPIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_SPIDEN_ENABLE: Enable + CPU1_DBGEN: CC_SOCU_DFLT_CPU1_DBGEN_ENABLE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_CPU1_DBGEN_DISABLE: Disable + # - CC_SOCU_DFLT_CPU1_DBGEN_ENABLE: Enable + CPU1_NIDEN: CC_SOCU_DFLT_CPU1_NIDEN_ENABLE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug fixed state + # Possible Enumeration values: + # - CC_SOCU_DFLT_CPU1_NIDEN_DISABLE: Disable + # - CC_SOCU_DFLT_CPU1_NIDEN_ENABLE: Enable + SECURE_BOOT_CFG: + bitfields: # The register bitfields + SKIP_DICE: 1 # Width: 2b[0-3], Description: Skip DICE computation. 00 - Enable DICE 01,10,11 - Disable DICE + SEC_BOOT_EN: 1 # Width: 2b[0-3], Description: Secure boot enable. 00 - Plain image (internal flash with or without CRC) 01, 10, 11 - Boot signed images. (internal flash, RSA signed) + ROTKH: + # Reg Description: ROTKH0 for Root of Trust Keys Table hash[255:224] ROTKH1 for Root of Trust Keys Table hash[223:192] ROTKH2 for Root of Trust Keys Table hash[191:160] ROTKH3 for Root of Trust Keys Table hash[159:128] ROTKH4 for Root of Trust Keys Table hash[127:96] ROTKH5 for Root of Trust Keys Table hash[95:64] ROTKH6 for Root of Trust Keys Table hash[63:32] ROTKH7 for Root of Trust Keys Table hash[31:0] + value: ea5c35049dd077472cad747638dd478e4e435ca047fc62562b0b3ab83c2ad250 # The value width: 256b diff --git a/tests/pfr/data/cmpa_pfrc.json b/tests/pfr/data/cmpa_pfrc.json index 169877a1..077cb075 100644 --- a/tests/pfr/data/cmpa_pfrc.json +++ b/tests/pfr/data/cmpa_pfrc.json @@ -24,8 +24,7 @@ "TAPEN": "0b0", "CPU1_DBGEN": "0b0", "ISP_CMD_EN": "0b1", - "FA_CMD_EN": "0b0", - "ME_CMD_EN": "0b0", + "PMCMD_EN": "0b0", "CPU1_NIDEN": "0b0", "UUID_CHECK": "0b0", "INVERSE_VALUE": "0xffbf" @@ -38,8 +37,7 @@ "TAPEN": "0b0", "CPU1_DBGEN": "0b0", "ISP_CMD_EN": "0b1", - "FA_CMD_EN": "0b0", - "ME_CMD_EN": "0b0", + "PMCMD_EN": "0b0", "CPU1_NIDEN": "0b0", "INVERSE_VALUE": "0xffbf" }, diff --git a/tests/pfr/data/empty_file b/tests/pfr/data/empty_file new file mode 100644 index 00000000..e69de29b diff --git a/tests/pfr/data/empty_json.json b/tests/pfr/data/empty_json.json new file mode 100644 index 00000000..544b7b4d --- /dev/null +++ b/tests/pfr/data/empty_json.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/pfr/data/empty_json1.json b/tests/pfr/data/empty_json1.json new file mode 100644 index 00000000..06fe22c2 --- /dev/null +++ b/tests/pfr/data/empty_json1.json @@ -0,0 +1,3 @@ +{ + "invalid": "invalid" +} \ No newline at end of file diff --git a/tests/pfr/data/empty_yml.yml b/tests/pfr/data/empty_yml.yml new file mode 100644 index 00000000..7a6430dd --- /dev/null +++ b/tests/pfr/data/empty_yml.yml @@ -0,0 +1 @@ +invalid: "invalid" diff --git a/tests/pfr/data/rules.json b/tests/pfr/data/rules.json index af87a331..2047150f 100644 --- a/tests/pfr/data/rules.json +++ b/tests/pfr/data/rules.json @@ -1,14 +1,14 @@ [ { "req_id": "0.0", - "desc": "Something about SECURE DICE SHIT or whatever", - "msg": "You need to enable some shit and disable some other shit", + "desc": "Something about SECURE DICE or whatever", + "msg": "You need to enable something and disable something other", "cond": "CMPA.SECURE_BOOT_CFG.SEC_BOOT_EN != 0 and CMPA.SECURE_BOOT_CFG.SKIP_DICE == 0" }, { "req_id": "0.1", - "desc": "Something about SECURE DICE SHIT or whatever", - "msg": "You need to enable some shit and disable some other shit", + "desc": "Something about SECURE DICE or whatever", + "msg": "You need to enable something and disable something other", "cond": "CMPA.SECURE_BOOT_CFG.SEC_BOOT_EN != 0 and UTIL.hasDigestSet() == False" }, { @@ -67,8 +67,8 @@ }, { "req_id": "2.1", - "desc": "This CMPA_PROG_IN_PROGRESS must be always 0x00000000. Only ROM bootlader is allowed to write anything to this field.", - "msg": "The CMPA_PROG_IN_PROGESS must be set to 0!", + "desc": "This CMPA_PROG_IN_PROGRESS must be always 0x00000000. Only ROM bootloader is allowed to write anything to this field.", + "msg": "The CMPA_PROG_IN_PROGRESS must be set to 0!", "cond": "CFPA.CMPA_PROG_IN_PROGRESS != 0" }, { diff --git a/tests/pfr/test_bitarray.py b/tests/pfr/test_bitarray.py index bd8cd71b..da1a218e 100644 --- a/tests/pfr/test_bitarray.py +++ b/tests/pfr/test_bitarray.py @@ -1,28 +1,31 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause - +"""Test of bit array module.""" from bitstring import BitArray def test_basic(): - ba = BitArray(f"uint:32={0x1111}") - assert len(ba) == 32 - bl = ba[16:] - assert len(bl) == 16 - assert bl.hex == '1111' + """ Test of BitArray - Basic test.""" + bit_array = BitArray(f"uint:32={0x1111}") + assert len(bit_array) == 32 + bit_low = bit_array[16:] + assert len(bit_low) == 16 + assert bit_low.hex == '1111' def test_invert(): - ba = BitArray(f"uint:32={0x1111}") - bl = ba[16:] - bl.invert() - assert bl.hex == 'eeee' - ba.overwrite(bl, 0) - assert ba.hex == 'eeee1111' + """ Test of BitArray - Invert.""" + bit_array = BitArray(f"uint:32={0x1111}") + bit_low = bit_array[16:] + bit_low.invert() + assert bit_low.hex == 'eeee' + bit_array.overwrite(bit_low, 0) + assert bit_array.hex == 'eeee1111' def test_byteswap(): - ba = BitArray(f"uint:32={0x1234_5678}") - ba.byteswap() - assert ba.hex == '78563412' + """ Test of BitArray - Byte swap.""" + bit_array = BitArray(f"uint:32={0x1234_5678}") + bit_array.byteswap() + assert bit_array.hex == '78563412' diff --git a/tests/pfr/test_cli_utils.py b/tests/pfr/test_cli_utils.py index 18d90cd7..b8f57d62 100644 --- a/tests/pfr/test_cli_utils.py +++ b/tests/pfr/test_cli_utils.py @@ -1,23 +1,21 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause +"""Test CLI utilities.""" from os import path -import json -import jsonschema import pytest from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import load_pem_public_key -from spsdk.apps.pfr import _extract_public_key, _load_user_config -from spsdk.apps.pfr import _get_data_for_html, _generate_html -from spsdk.pfr import CMPA +from spsdk.apps.pfr import _extract_public_key from spsdk.utils.misc import use_working_directory def read_file(data_dir, file_name, mode='r'): + """Help function to load file.""" with open(path.join(data_dir, file_name), mode) as f: return f.read() @@ -27,7 +25,7 @@ def test_extract_public_key(data_dir): public_key_data = read_file(data_dir, 'public.pem', 'rb') public_key = load_pem_public_key(public_key_data, default_backend()) public_nums = public_key.public_numbers() - + with use_working_directory(data_dir): numbers = _extract_public_key('public.pem', password=None).public_numbers() assert public_nums == numbers @@ -38,24 +36,7 @@ def test_extract_public_key(data_dir): def test_unsupported_secret_type(data_dir): + """Test unsupported secret type.""" with use_working_directory(data_dir): with pytest.raises(AssertionError): _extract_public_key('cfpa_test.json', password=None) - - -def test_no_user_config(): - assert _load_user_config(None) is None - - -def test_get_data_for_html(data_dir): - data = _get_data_for_html(CMPA('lpc55s6x')) - schema = json.loads(read_file(data_dir, 'html_data.schema')) - # in case of a failure, an exception is thrown - jsonschema.validate(data, schema) - assert True - - -def test_generate_html(): - data = _get_data_for_html(CMPA('lpc55s6x')) - html = _generate_html('CMPA', data) - assert "

CMPA

" in html diff --git a/tests/pfr/test_pfr.py b/tests/pfr/test_pfr.py index 32a8a85a..58f30a6e 100644 --- a/tests/pfr/test_pfr.py +++ b/tests/pfr/test_pfr.py @@ -1,47 +1,69 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -import json +"""The test file for PFR API.""" +import os +import filecmp +from spsdk.pfr.exceptions import SPSDKPfrRotkhIsNotPresent import pytest + from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import load_pem_private_key - -from spsdk.pfr import CFPA, CMPA +from ruamel.yaml import YAML + +from spsdk.exceptions import SPSDKError +from spsdk.pfr import ( + CFPA, + CMPA, + PfrConfiguration, + SPSDKPfrConfigReadError, + SPSDKPfrConfigError +) from spsdk.utils.misc import load_file def test_generate_cmpa(data_dir): - config = json.loads(load_file(data_dir, 'cmpa_96mhz.json')) + """Test PFR tool - Generating CMPA binary.""" binary = load_file(data_dir, 'CMPA_96MHz.bin', mode='rb') key = load_pem_private_key( load_file(data_dir, 'selfsign_privatekey_rsa2048.pem', mode='rb'), password=None, backend=default_backend()) - cmpa = CMPA('lpc55s6x', keys=[key.public_key()], user_config=config['settings']) - assert binary == cmpa.export(add_seal=False, compute_inverses=True) + pfr_cfg_json = PfrConfiguration(os.path.join(data_dir, 'cmpa_96mhz.json')) + cmpa_json = CMPA('lpc55s6x', user_config=pfr_cfg_json) + assert binary == cmpa_json.export(add_seal=False, keys=[key.public_key()]) + + pfr_cfg_yml = PfrConfiguration(os.path.join(data_dir, 'cmpa_96mhz.yml')) + cmpa_yml = CMPA('lpc55s6x', user_config=pfr_cfg_yml) + assert binary == cmpa_yml.export(add_seal=False, keys=[key.public_key()]) def test_generate_cfpa(data_dir): - config = json.loads(load_file(data_dir, 'cfpa_test.json')) + """Test PFR tool - Generating CFPA binary.""" binary = load_file(data_dir, 'CFPA_test.bin', mode='rb') - cfpa = CFPA('lpc55s6x', user_config=config['settings']) - data = cfpa.export(add_seal=True, compute_inverses=False) - assert binary == data + pfr_cfg_json = PfrConfiguration(os.path.join(data_dir, 'cfpa_test.json')) + cfpa_json = CFPA('lpc55s6x', user_config=pfr_cfg_json) + assert cfpa_json.export(add_seal=True) == binary + + pfr_cfg_yml = PfrConfiguration(os.path.join(data_dir, 'cfpa_test.yml')) + cfpa_yml = CFPA('lpc55s6x', user_config=pfr_cfg_yml) + assert cfpa_yml.export(add_seal=True) == binary def test_supported_devices(): + """Test PFR tool - Getting supported devices.""" cfpa_devices = CFPA.devices() cmpa_devices = CMPA.devices() - assert sorted(cmpa_devices) == sorted(cfpa_devices) def test_seal_cfpa(): + """Test PFR tool - Test CFPA seal.""" cfpa = CFPA('lpc55s6x') data = cfpa.export(add_seal=False) @@ -54,6 +76,7 @@ def test_seal_cfpa(): def test_seal_cmpa_n4analog(): + """Test PFR tool - Test CMPA seal on Niobe 4Analog.""" cfpa = CFPA('lpc55s3x') data = cfpa.export(add_seal=False) @@ -66,38 +89,159 @@ def test_seal_cmpa_n4analog(): def test_basic_cmpa(): - cmpa = CMPA('lpc55s6x') - with pytest.raises(AssertionError): - cmpa.export() + """Test PFR tool - Test CMPA basis.""" + CMPA('lpc55s6x') def test_config_cfpa(): + """Test PFR tool - Test CFPA configuration.""" cfpa = CFPA('lpc55s6x') config = cfpa.generate_config() config2 = cfpa.generate_config(exclude_computed=False) assert config != config2 - cfpa2 = CFPA('lpc55s6x', user_config=config2) - out = cfpa2.parse(bytes(512), exclude_computed=False) - + cfpa2 = CFPA('lpc55s6x', user_config=PfrConfiguration(config2)) + cfpa2.parse(bytes(512), exclude_computed=False) + out = cfpa2.get_yaml_config(exclude_computed=False) assert out == config2 def test_config_cmpa(): + """Test PFR tool - Test CMPA configuration.""" cmpa = CMPA('lpc55s6x') config = cmpa.generate_config() config2 = cmpa.generate_config(exclude_computed=False) assert config != config2 - cmpa2 = CMPA('lpc55s6x', user_config=config2) - out = cmpa2.parse(bytes(512), exclude_computed=False) + cmpa2 = CMPA('lpc55s6x', user_config=PfrConfiguration(config2)) + cmpa2.parse(bytes(512), exclude_computed=False) + out = cmpa2.get_yaml_config(exclude_computed=False) assert out == config2 - -def test_address(): +def test_config_cmpa_yml(tmpdir): + """Test PFR tool - Test CMPA configuration from YAML.""" + yaml = YAML() + yaml.indent(sequence=4, offset=2) cmpa = CMPA('lpc55s6x') - assert '0x9_E400' == cmpa.get_address(remove_underscore=False) - assert '0x9E400' == cmpa.get_address(remove_underscore=True) + config = cmpa.get_yaml_config(exclude_computed=True) + with open(tmpdir+"\\config.yml", 'w') as yml_file: + yaml.dump(config, yml_file) + + config2 = cmpa.get_yaml_config(exclude_computed=False) + with open(tmpdir+"\\config2.yml", 'w') as yml_file: + yaml.dump(config2, yml_file) + + assert not filecmp.cmp(tmpdir+"\\config.yml", tmpdir+"\\config2.yml") + + cmpa2 = CMPA('lpc55s6x') + cmpa2_pfr_cfg = PfrConfiguration(tmpdir+"\\config.yml") + cmpa2.set_config(cmpa2_pfr_cfg) + out_config = cmpa2.get_yaml_config(exclude_computed=True) + with open(tmpdir+"\\out_config.yml", 'w') as yml_file: + yaml.dump(out_config, yml_file) + + assert filecmp.cmp(tmpdir+"\\config.yml", tmpdir+"\\out_config.yml") + + cmpa2_pfr_cfg = PfrConfiguration(tmpdir+"\\config2.yml") + cmpa2.set_config(cmpa2_pfr_cfg, raw=True) + out_config2 = cmpa2.get_yaml_config(exclude_computed=False) + with open(tmpdir+"\\out_config2.yml", 'w') as yml_file: + yaml.dump(out_config2, yml_file) + + assert filecmp.cmp(tmpdir+"\\config2.yml", tmpdir+"\\out_config2.yml") + +def test_load_config(): + """Test just initialization of PFR config.""" + assert PfrConfiguration() + +def test_load_config_invalid(data_dir): + """Test PFR tool - PFR Configuration Invalid cases.""" + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/invalid_file") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/invalid_file.yml") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/invalid_file.json") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/empty_json.json") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/empty_json1.json") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/empty_yml.yml") + + with pytest.raises(SPSDKPfrConfigReadError): + PfrConfiguration(data_dir+"/empty_file") + +def test_set_config_invalid(data_dir): + """Test invalid cases for set_config.""" + cmpa = CMPA(device="lpc55s6x") + cfg = PfrConfiguration(data_dir+"/bad_dev.yml") + with pytest.raises(SPSDKPfrConfigError): + cmpa.set_config(cfg) + + cfg = PfrConfiguration(data_dir+"/bad_rev.yml") + with pytest.raises(SPSDKPfrConfigError): + cmpa.set_config(cfg) + + cfg = PfrConfiguration() + cfg.device = cmpa.device + cfg.revision = cmpa.revision + cfg.type = "INV" + with pytest.raises(SPSDKPfrConfigError): + cmpa.set_config(cfg) + + cfg = PfrConfiguration() + cfg.device = cmpa.device + cfg.revision = cmpa.revision + cfg.type = "CMPA" + cfg.file_type = "INV" + with pytest.raises(SPSDKPfrConfigError): + cmpa.set_config(cfg) + +def test_invalid_computed_field_handler(): + """Test invalid case for computed filed handler.""" + cmpa = CMPA(device="lpc55s6x") + fields = {"test_field":"invalid_handler"} + + with pytest.raises(SPSDKError): + cmpa.reg_computed_fields_handler(b'\x00', fields) + +def test_get_bitfields_ignore(): + """Test invalid case for computed filed handler.""" + cmpa = CMPA(device="lpc55s6x") + cmpa.config.config.pop('ignored_fields', None) + assert cmpa.generate_config() + +def test_json_yml_configs(data_dir): + """Test of JSON and YML configuration, it must be equal.""" + cmpa_json = CMPA('lpc55s6x', user_config=PfrConfiguration(f"{data_dir}/cmpa_96mhz.json")) + cmpa_yml = CMPA('lpc55s6x', user_config=PfrConfiguration(f"{data_dir}/cmpa_96mhz.yml")) + + assert cmpa_yml.get_yaml_config(False) == cmpa_json.get_yaml_config(False) + assert cmpa_yml.get_json_config(False) == cmpa_json.get_json_config(False) + +def test_without_ignored_bitfields(data_dir): + """Test of CMPA configuration without ignored bitfields.""" + cmpa = CMPA('lpc55s6x', user_config=PfrConfiguration(f"{data_dir}/cmpa_96mhz.yml")) + + cmpa_with_ignored_bitfields_yml = cmpa.get_yaml_config(False) + cmpa_with_ignored_bitfields_json = cmpa.get_json_config(False) + cmpa.config.config.pop("ignored_fields") + cmpa_without_ignored_bitfields_yml = cmpa.get_yaml_config(False) + cmpa_without_ignored_bitfields_json = cmpa.get_json_config(False) + assert cmpa_with_ignored_bitfields_yml != cmpa_without_ignored_bitfields_yml + assert cmpa_with_ignored_bitfields_json != cmpa_without_ignored_bitfields_json + +def test_missing_rotkh(): + """Simple test to check right functionality of missing ROTKH.""" + cfpa = CFPA('lpc55s6x') + with pytest.raises(SPSDKPfrRotkhIsNotPresent): + cfpa.export(keys=["Invalid"]) diff --git a/tests/pfr/test_pfr_cli.py b/tests/pfr/test_pfr_cli.py index f3fb1ddd..b5717afb 100644 --- a/tests/pfr/test_pfr_cli.py +++ b/tests/pfr/test_pfr_cli.py @@ -1,18 +1,17 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Tests for `pfr` application.""" import filecmp -import json import logging import os - from click.testing import CliRunner +from spsdk.pfr.pfr import PfrConfiguration from spsdk.apps import pfr as cli from spsdk.apps import spsdk_apps from spsdk.pfr import CMPA @@ -28,6 +27,7 @@ def test_command_line_interface(): def test_cli_devices(): + """Test PFR CLI - devices.""" runner = CliRunner() result = runner.invoke(cli.main, ['devices']) for device in CMPA.devices(): @@ -35,6 +35,7 @@ def test_cli_devices(): def test_cli_devices_global(): + """Test PFR CLI - devices from global space.""" runner = CliRunner() result = runner.invoke(spsdk_apps.main, ['pfr', 'devices']) for device in CMPA.devices(): @@ -42,7 +43,8 @@ def test_cli_devices_global(): def test_generate_cmpa(data_dir, tmpdir): - cmd = f'generate --output {tmpdir}/pnd.bin ' + """Test PFR CLI - Generation CMPA binary.""" + cmd = f'generate-binary --output {tmpdir}/pnd.bin ' cmd += f'--user-config {data_dir}/cmpa_96mhz.json --calc-inverse ' cmd += f'--secret-file {data_dir}/selfsign_privatekey_rsa2048.pem ' logging.debug(cmd) @@ -55,16 +57,17 @@ def test_generate_cmpa(data_dir, tmpdir): def test_generate_cmpa_with_elf2sb(data_dir, tmpdir): + """Test PFR CLI - Generation CMPA binary with elf2sb.""" org_file = f'{tmpdir}/org.bin' new_file = f'{tmpdir}/new.bin' big_file = f'{tmpdir}/big.bin' - cmd = 'generate --user-config cmpa_96mhz.json' + cmd = 'generate-binary --user-config cmpa_96mhz.json' # basic usage when keys are passed on command line cmd1 = cmd + f' -o {org_file} -f rotk0_rsa_2048.pub -f rotk1_rsa_2048.pub' # elf2sb config file contains previous two keys + one empty line + 4th entry is not present cmd2 = cmd + f' -o {new_file} -e elf2sb_config.json' - # keys on commandline take precedence and pfr ignores elf2sb configuration + # keys on commandline are in exclusion with elf2sb configuration, the command fails cmd3 = cmd + f' -o {big_file} -e big_elf2sb_config.json -f rotk0_rsa_2048.pub -f rotk1_rsa_2048.pub' with use_working_directory(data_dir): result = CliRunner().invoke(cli.main, cmd1.split()) @@ -72,36 +75,41 @@ def test_generate_cmpa_with_elf2sb(data_dir, tmpdir): result = CliRunner().invoke(cli.main, cmd2.split()) assert result.exit_code == 0, result.output result = CliRunner().invoke(cli.main, cmd3.split()) - assert result.exit_code == 0, result.output + assert result.exit_code != 0, result.output assert filecmp.cmp(org_file, new_file) - assert filecmp.cmp(org_file, big_file) def test_parse(data_dir, tmpdir): - cmd = 'parse --device lpc55s6x --type cmpa ' + """Test PFR CLI - Parsing CMPA binary to get config.""" + cmd = 'parse-binary --device lpc55s6x --type cmpa ' cmd += f'--binary {data_dir}/CMPA_96MHz.bin ' cmd += f'--show-diff ' - cmd += f'--output {tmpdir}/config.json ' + cmd += f'--output {tmpdir}/config.yml' logging.debug(cmd) runner = CliRunner() result = runner.invoke(cli.main, cmd.split()) assert result.exit_code == 0, result.output - new_data = open(f'{tmpdir}/config.json', 'r').read() - expected = open(f'{data_dir}/cmpa_96mhz.json', 'r').read() + new_data = open(f'{tmpdir}/config.yml', 'r').read() + expected = open(f'{data_dir}/cmpa_96mhz_rotkh.yml', 'r').read() assert new_data == expected -def test_user_config(data_dir, tmpdir): - cmd = 'user-config --device lpc55s6x --type cmpa' +def test_user_config(tmpdir): + """Test PFR CLI - Generation CMPA user config.""" + cmd = f'get-cfg-template --device lpc55s6x --type cmpa --output {tmpdir}/cmpa.yml' logging.debug(cmd) runner = CliRunner() result = runner.invoke(cli.main, cmd.split()) assert result.exit_code == 0, result.output # verify that the output is a valid json object - assert json.loads(result.output) + pfr_config = PfrConfiguration(f"{tmpdir}/cmpa.yml") + assert pfr_config + assert pfr_config.type == "CMPA" + assert pfr_config.device == "lpc55s6x" def test_info(tmpdir): + """Test PFR CLI - Creating HTML fields information.""" cmd = f'info --device lpc55s6x --type cmpa --output {tmpdir}/cmpa.html' logging.debug(cmd) runner = CliRunner() diff --git a/tests/pfr/test_pfrc_cli.py b/tests/pfr/test_pfrc_cli.py index 0e62d4d1..3ac247a4 100644 --- a/tests/pfr/test_pfrc_cli.py +++ b/tests/pfr/test_pfrc_cli.py @@ -1,16 +1,17 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause - +"""Test module for PRFC CLI tool.""" from click.testing import CliRunner from spsdk.apps import pfrc def test_pfrc_basic_cli(data_dir): + """Test PFR Checker tool.""" cmd = f"-m {data_dir}/cmpa_pfrc.json -f {data_dir}/cfpa_pfrc.json -r {data_dir}/rules.json" result = CliRunner().invoke(pfrc.main, cmd.split()) assert result.exit_code == 0 diff --git a/tests/sbfile/test_commands_api.py b/tests/sbfile/test_commands_api.py index 555ca73b..a051b1fe 100644 --- a/tests/sbfile/test_commands_api.py +++ b/tests/sbfile/test_commands_api.py @@ -54,11 +54,47 @@ def test_load_cmd(): assert cmd == cmd_parsed -def test_fill_cmd(): - cmd = CmdFill(address=100, pattern=b'\x00\x01\x02\x00') +def test_load_cmd_preexisting(): + data = ( + b'\x1c\x02\x00\x00\n\x00\x00\x00\x10\x00\x00\x00_3<\xd8' + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\xbb\xffT\x0f+r' + ) + cmd = parse_command(data) + assert isinstance(cmd, CmdLoad) + assert cmd.address == 10 + assert cmd.data[:10] == bytes(range(10)) + + +def test_fill_cmd_byte_word(): + cmd = CmdFill(address=100, pattern=1, length=4) assert cmd.address == 100 - assert cmd.pattern == b'\x00\x01\x02\x00' - assert cmd.info() + assert cmd.pattern == b'\x01\x01\x01\x01' + + data = cmd.export() + assert len(data) == 16 + assert len(data) == cmd.raw_size + + cmd_parsed = parse_command(data) + assert cmd == cmd_parsed + + +def test_fill_cmd_half_word(): + cmd = CmdFill(address=100, pattern=258, length=12) + assert cmd.address == 100 + assert cmd.pattern == b'\x01\x02\x01\x02' + + data = cmd.export() + assert len(data) == 16 + assert len(data) == cmd.raw_size + + cmd_parsed = parse_command(data) + assert cmd == cmd_parsed + + +def test_fill_cmd_whole_word(): + cmd = CmdFill(address=100, pattern=16909060, length=8) + assert cmd.address == 100 + assert cmd.pattern == b'\x01\x02\x03\x04' data = cmd.export() assert len(data) == 16 @@ -67,22 +103,33 @@ def test_fill_cmd(): cmd_parsed = parse_command(data) assert cmd == cmd_parsed - cmd = CmdFill(address=100, pattern=b'\x00\x01\x02\x00\xFF\xFE\xFD\xFC') + +def test_fill_cmd_length_not_defined(): + cmd = CmdFill(address=100, pattern=16909060) + assert cmd.address == 100 + assert cmd.pattern == b'\x01\x02\x03\x04' + data = cmd.export() - assert len(data) == 32 + assert len(data) == 16 assert len(data) == cmd.raw_size cmd_parsed = parse_command(data) assert cmd == cmd_parsed + +def test_fill_cmd_empty_word(): with pytest.raises(ValueError): - CmdFill(address=100, pattern=b'') + CmdFill(address=100, pattern=0) + +def test_fill_cmd_incorrect_length(): with pytest.raises(ValueError): - CmdFill(address=100, pattern=b'\x00\x01') + CmdFill(address=100, pattern=0, length=9) + +def test_fill_cmd_incorrect_word(): with pytest.raises(ValueError): - CmdFill(address=100, pattern=b'\x00\x01\x02\x00\xFF') + CmdFill(address=100, pattern=283678294867452) def test_jump_cmd(): @@ -100,6 +147,17 @@ def test_jump_cmd(): assert cmd == cmd_parsed +def test_jump_cmd_with_spreg(): + cmd = CmdJump(address=200, argument=50, spreg=32) + assert cmd.address == 200 + assert cmd.argument == 50 + assert cmd.spreg == 32 + assert cmd._header.count == 32 + assert cmd._header.flags == 2 + assert cmd._header.address == 200 + assert cmd._header.data == 50 + + def test_call_cmd(): cmd = CmdCall(address=100, argument=10) assert cmd.address == 100 diff --git a/tests/shadowregs/conftest.py b/tests/shadowregs/conftest.py index 432236a9..b25f1ae9 100644 --- a/tests/shadowregs/conftest.py +++ b/tests/shadowregs/conftest.py @@ -4,8 +4,7 @@ # Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -from os import path -import pytest +"""Test module to test stuff around Shadow registers.""" from spsdk.debuggers.utils import PROBES from tests.debuggers.debug_probe_virtual import DebugProbeVirtual diff --git a/tests/shadowregs/data/sh_test_dev.json b/tests/shadowregs/data/sh_test_dev.json index 1f8e430a..07ffd865 100644 --- a/tests/shadowregs/data/sh_test_dev.json +++ b/tests/shadowregs/data/sh_test_dev.json @@ -11,7 +11,7 @@ }, "computed_fields": { "REG1": {"CMP1": "comalg_dcfg_cc_socu_rsvd", "CRC8": "comalg_dcfg_cc_socu_crc8"}, - "REG2": {"RESERVED": "comalg_do_nothig"} + "REG2": {"RESERVED": "comalg_do_nothing"} } } } diff --git a/tests/shadowregs/data/test_database.json b/tests/shadowregs/data/test_database.json index 1f8e430a..d48aee36 100644 --- a/tests/shadowregs/data/test_database.json +++ b/tests/shadowregs/data/test_database.json @@ -10,8 +10,8 @@ "REG1": "REG_INVERTED_AP" }, "computed_fields": { - "REG1": {"CMP1": "comalg_dcfg_cc_socu_rsvd", "CRC8": "comalg_dcfg_cc_socu_crc8"}, - "REG2": {"RESERVED": "comalg_do_nothig"} + "REG1": {"RSRVD": "comalg_dcfg_cc_socu_rsvd", "CRC8": "comalg_dcfg_cc_socu_crc8"}, + "REG2": {"RESERVED": "comalg_do_nothing"} } } } diff --git a/tests/shadowregs/data/test_database_invalid_computed.json b/tests/shadowregs/data/test_database_invalid_computed.json index 4771d845..721a2470 100644 --- a/tests/shadowregs/data/test_database_invalid_computed.json +++ b/tests/shadowregs/data/test_database_invalid_computed.json @@ -11,7 +11,7 @@ }, "computed_fields": { "REG1": {"CMP1": "comalg_dcfg_cc_socu_invalid", "CRC8": "comalg_dcfg_cc_socu_crc8"}, - "REG2": {"RESERVED": "comalg_do_nothig"} + "REG2": {"RESERVED": "comalg_do_nothing"} } } } diff --git a/tests/shadowregs/test_registers.py b/tests/shadowregs/test_registers.py deleted file mode 100644 index cdbc84da..00000000 --- a/tests/shadowregs/test_registers.py +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 2021 NXP -# -# SPDX-License-Identifier: BSD-3-Clause -""" Tests for registers utility.""" - -import os -import filecmp -import pytest - -from spsdk.utils.registers import (BitfieldNotFound, EnumNotFound, RegConfig, Registers, - RegsRegister, - RegsBitField, - RegsEnum, - RegisterNotFound) - -from spsdk.utils.misc import use_working_directory - -TEST_DEVICE_NAME = "TestDevice1" -TEST_REG_NAME = "TestReg" -TEST_REG_OFFSET = 1024 -TEST_REG_WIDTH = 32 -TEST_REG_DESCR = "TestReg Description" -TEST_REG_REV = False -TEST_REG_ACCESS = "RW" -TEST_REG_VALUE = 0xA5A5A5A5 - -TEST_BITFIELD_NAME = "TestBitfiled" -TEST_BITFILED_OFFSET = 0x0F -TEST_BITFILED_WIDTH = 5 -TEST_BITFIELD_RESET_VAL = 33 -TEST_BITFIELD_ACCESS = "RW" -TEST_BITFIELD_DESCR = "Test Bitfield Description" -TEST_BITFIELD_SAVEVAL = 29 -TEST_BITFIELD_OUTOFRANGEVAL = 70 - -TEST_ENUM_NAME = "TestEnum" -TEST_ENUM_VALUE_BIN = "0b10001" -TEST_ENUM_VALUE_HEX = "0x11" -TEST_ENUM_VALUE_STRINT = "017" -TEST_ENUM_VALUE_INT = 17 -TEST_ENUM_VALUE_BYTES = b'\x11' -TEST_ENUM_RES_VAL = "0b010001" -TEST_ENUM_DESCR = "Test Enum Description" -TEST_ENUM_MAXWIDTH = 6 - -TEST_XML_FILE = "unit_test.xml" - -def test_basic_regs(tmpdir): - """Basic test of registers class.""" - regs = Registers(TEST_DEVICE_NAME) - - assert regs.dev_name == TEST_DEVICE_NAME - - reg1 = RegsRegister(TEST_REG_NAME, TEST_REG_OFFSET, TEST_REG_WIDTH, TEST_REG_DESCR, TEST_REG_REV, TEST_REG_ACCESS) - - with pytest.raises(RegisterNotFound): - regs.find_reg("NonExisting") - - # Ther Registers MUST return empty erray - assert regs.get_reg_names() == [] - - with pytest.raises(TypeError): - regs.remove_register("String") - - with pytest.raises(ValueError): - regs.remove_register(reg1) - - # Now we could do tests with a register added to list - regs.add_register(reg1) - - regs.remove_register_by_name(["String"]) - - assert TEST_REG_NAME in regs.get_reg_names() - - regt = regs.find_reg(TEST_REG_NAME) - - assert regt == reg1 - - with pytest.raises(TypeError): - regs.add_register("Invalid Parameter") - - regt.set_value(TEST_REG_VALUE) - assert reg1.get_value() == TEST_REG_VALUE.to_bytes(4,"big") - - filename = os.path.join(tmpdir, TEST_XML_FILE) - regs.write_xml(filename) - assert os.path.isfile(filename) - - printed_str = str(regs) - - assert TEST_DEVICE_NAME in printed_str - assert TEST_REG_NAME in printed_str - - regs.remove_register_by_name([TEST_REG_NAME]) - - with pytest.raises(RegisterNotFound): - regs.find_reg(TEST_REG_NAME) - assert False - -def test_register(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) - bitfield.add_enum(enum) - - parent_reg.add_bitfield(bitfield) - - printed_str = str(parent_reg) - - assert "Name:" in printed_str - assert TEST_REG_NAME in printed_str - assert TEST_REG_DESCR in printed_str - assert "Width:" in printed_str - assert "Access:" in printed_str - assert "Bitfield" in printed_str - assert TEST_BITFIELD_NAME in printed_str - assert TEST_BITFIELD_DESCR in printed_str - assert TEST_ENUM_NAME in printed_str - assert TEST_ENUM_DESCR in printed_str - -def test_register_invalid_val(): - reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - reg.set_value("Invalid") - assert reg.get_value() == b'' - - reg.set_value([1, 2]) - assert reg.get_value() == b'' - -def test_enum(): - enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) - - printed_str = str(enum) - - assert "Name:" in printed_str - assert "Value:" in printed_str - assert "Description:" in printed_str - assert TEST_ENUM_NAME in printed_str - assert "0b0" in printed_str - assert TEST_ENUM_DESCR in printed_str - -def test_enum_bin(): - enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_BIN, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert TEST_ENUM_RES_VAL in printed_str - -def test_enum_hex(): - enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_HEX, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert TEST_ENUM_RES_VAL in printed_str - -def test_enum_strint(): - enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_STRINT, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert TEST_ENUM_RES_VAL in printed_str - -def test_enum_int(): - enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_INT, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert TEST_ENUM_RES_VAL in printed_str - -def test_enum_bytes(): - enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_BYTES, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert TEST_ENUM_RES_VAL in printed_str - -def test_enum_invalidval(): - try: - enum = RegsEnum(TEST_ENUM_NAME, "InvalidValue", TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) - printed_str = str(enum) - assert "N/A" in printed_str - except TypeError: - assert 0 - -def test_bitfield(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) - bitfield.add_enum(enum) - - parent_reg.add_bitfield(bitfield) - - printed_str = str(bitfield) - - assert "Name:" in printed_str - assert "Offset:" in printed_str - assert "Width:" in printed_str - assert "Access:" in printed_str - assert "Reset val:" in printed_str - assert "Description:" in printed_str - assert "Enum" in printed_str - -def test_bitfield_find(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) - bitfield.add_enum(enum) - - parent_reg.add_bitfield(bitfield) - - assert bitfield == parent_reg.find_bitfield(TEST_BITFIELD_NAME) - - with pytest.raises(BitfieldNotFound): - parent_reg.find_bitfield("Invalid Name") - -def test_bitfield_has_enums(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - parent_reg.add_bitfield(bitfield) - - assert bitfield.has_enums() is False - enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) - bitfield.add_enum(enum) - - assert bitfield.has_enums() is True - - assert enum in bitfield.get_enums() - -def test_bitfield_value(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - bitfield.set_value(TEST_BITFIELD_SAVEVAL) - assert bitfield.get_value() == TEST_BITFIELD_SAVEVAL - - with pytest.raises(ValueError): - bitfield.set_value(TEST_BITFIELD_OUTOFRANGEVAL) - -def test_bitfield_enums(): - parent_reg = RegsRegister(TEST_REG_NAME, - TEST_REG_OFFSET, - TEST_REG_WIDTH, - TEST_REG_DESCR, - TEST_REG_REV, - TEST_REG_ACCESS) - - bitfield = RegsBitField(parent_reg, - TEST_BITFIELD_NAME, - TEST_BITFILED_OFFSET, - TEST_BITFILED_WIDTH, - TEST_BITFIELD_DESCR, - TEST_BITFIELD_RESET_VAL, - TEST_BITFIELD_ACCESS) - - parent_reg.add_bitfield(bitfield) - - enums = [] - for n in range((1 << TEST_BITFILED_WIDTH)-1): - enum = RegsEnum(f"{TEST_ENUM_NAME}{n}", n, f"{TEST_ENUM_DESCR}{n}", TEST_BITFILED_WIDTH) - enums.append(enum) - bitfield.add_enum(enum) - - enum_names = bitfield.get_enum_names() - - for n in range((1 << TEST_BITFILED_WIDTH)-1): - assert n == bitfield.get_enum_constant(f"{TEST_ENUM_NAME}{n}") - assert enums[n].name in enum_names - - for n in range((1 << TEST_BITFILED_WIDTH)): - bitfield.set_value(n) - if n < (1 << TEST_BITFILED_WIDTH)-1: - assert f"{TEST_ENUM_NAME}{n}" == bitfield.get_enum_value() - else: - assert n == bitfield.get_enum_value() - - for n in range((1 << TEST_BITFILED_WIDTH)-1): - bitfield.set_enum_value(f"{TEST_ENUM_NAME}{n}") - assert n == bitfield.get_value() - - with pytest.raises(EnumNotFound): - bitfield.get_enum_constant("Invalid name") - - regs = Registers(TEST_DEVICE_NAME) - - regs.add_register(parent_reg) - -def test_registers_xml(data_dir, tmpdir): - regs = Registers(TEST_DEVICE_NAME) - - with use_working_directory(data_dir): - regs.load_registers_from_xml("registers.xml") - - with use_working_directory(tmpdir): - regs.write_xml("registers.xml") - - regs2 = Registers(TEST_DEVICE_NAME) - - with use_working_directory(tmpdir): - regs2.load_registers_from_xml("registers.xml") - - assert str(regs) == str(regs2) - -def test_registers_corrupted_xml(data_dir, tmpdir): - regs = Registers(TEST_DEVICE_NAME) - - with use_working_directory(data_dir): - regs.load_registers_from_xml("registers_corr.xml") - - with use_working_directory(tmpdir): - regs.write_xml("registers_corr.xml") - - assert not filecmp.cmp(os.path.join(data_dir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr.xml")) - - regs.clear() - - with use_working_directory(tmpdir): - regs.load_registers_from_xml("registers_corr.xml") - regs.write_xml("registers_corr1.xml") - - assert filecmp.cmp(os.path.join(tmpdir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr1.xml")) - - # Without clear - Cannot add register with same name as is already added - with use_working_directory(tmpdir): - regs.load_registers_from_xml("registers_corr.xml") - regs.write_xml("registers_corr1.xml") - - assert filecmp.cmp(os.path.join(tmpdir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr1.xml")) - -def test_reg_config_get_devices(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - devices = reg_config.get_devices() - - assert "test_device1" in devices - assert "test_device2" in devices - -def test_reg_config_get_devices_class(data_dir): - devices = RegConfig.devices(os.path.join(data_dir, "reg_config.json")) - - assert "test_device1" in devices - assert "test_device2" in devices - -def test_reg_config_get_latest_revision(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - rev = reg_config.get_latest_revision("test_device1") - assert rev == "x1" - - rev = reg_config.get_latest_revision("test_device2") - assert rev == "b0" - -def test_reg_config_get_revisions(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - revs = reg_config.get_revisions("test_device1") - assert "x0" in revs - assert "x1" in revs - - revs = reg_config.get_revisions("test_device2") - assert "b0" in revs - -def test_reg_config_get_address(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - addr = reg_config.get_address("test_device1") - assert addr == "0xA5A5_1234" - - addr = reg_config.get_address("test_device2", remove_underscore=True) - assert addr == "0x40000000" - -def test_reg_config_get_data_file(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - data_file = reg_config.get_data_file("test_device1", "x0") - assert os.path.join(data_dir, "test_device1_x0.xml") == data_file - - data_file = reg_config.get_data_file("test_device1", "x1") - assert os.path.join(data_dir, "test_device1_x1.xml") == data_file - - data_file = reg_config.get_data_file("test_device2", "b0") - assert os.path.join(data_dir, "test_device2_b0.xml") == data_file - -def test_reg_config_get_antipoleregs(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - antipole = reg_config.get_antipole_regs("test_device1") - assert antipole["INVERTED_REG"] == "INVERTED_REG_AP" - - antipole = reg_config.get_antipole_regs("test_device2") - assert antipole["INVERTED_REG"] == "INVERTED_REG_AP" - -def test_reg_config_get_computed_fields(data_dir): - reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) - - computed_fields = reg_config.get_computed_fields("test_device1") - assert computed_fields["COMPUTED_REG"]["TEST_FIELD1"] == "computed_reg_test_field1" - assert computed_fields["COMPUTED_REG"]["TEST_FIELD2"] == "computed_reg_test_field2" - assert computed_fields["COMPUTED_REG2"]["TEST_FIELD1"] == "computed_reg2_test_field1" - assert computed_fields["COMPUTED_REG2"]["TEST_FIELD2"] == "computed_reg2_test_field2" - diff --git a/tests/shadowregs/test_shadowregs.py b/tests/shadowregs/test_shadowregs.py index f3df3db2..c99b9be7 100644 --- a/tests/shadowregs/test_shadowregs.py +++ b/tests/shadowregs/test_shadowregs.py @@ -4,12 +4,13 @@ # Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -""" Tests for nxpkeygen utility.""" +""" Tests for shadow registers support API.""" import os import pytest from spsdk.exceptions import SPSDKError -import spsdk.dat.shadow_regs as SR +from spsdk.utils.reg_config import RegConfig +import spsdk.shadowregs.shadowregs as SR import spsdk.utils.registers as REGS import spsdk.debuggers.debug_probe as DP @@ -22,21 +23,25 @@ TEST_DATABASE_BAD_COMPUTED_FUNC = "test_database_invalid_computed.json" def get_probe(): + """Help function to get Probe - used in tests.""" probe = DebugProbeVirtual(DebugProbeVirtual.UNIQUE_SERIAL) probe.open() probe.enable_memory_interface() return probe def get_registers(xml_filename, filter_reg=None): + """Help function to get Registers - used in tests.""" registers = REGS.Registers(TEST_DEV_NAME) registers.load_registers_from_xml(xml_filename, filter_reg=filter_reg) return registers def get_config(database_filename): - config = SR.RegConfig(database_filename) + """Help function to get RegConfig - used in tests.""" + config = RegConfig(database_filename) return config def test_shadowreg_basic(data_dir): + """Test Shadow Registers - Basic test.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) @@ -44,13 +49,14 @@ def test_shadowreg_basic(data_dir): assert shadowregs.device == TEST_DEV_NAME def test_shadowreg_set_get_reg(data_dir): + """Test Shadow Registers - Setting and getting register.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) test_val = bytearray(32) - for i, val in enumerate(test_val): + for i in range(32): test_val[i] = i shadowregs.set_register("REG1", 0x12345678) @@ -66,6 +72,7 @@ def test_shadowreg_set_get_reg(data_dir): assert shadowregs.get_register("REG_BIG_REV") == test_val def test_shadowreg_set_reg_invalid(data_dir): + """Test Shadow Registers - INVALID cases of set and get registers.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) @@ -77,6 +84,7 @@ def test_shadowreg_set_reg_invalid(data_dir): shadowregs.set_register("REG1_Invalid", 0x12345678) def test_shadowreg_get_reg_invalid(data_dir): + """Test Shadow Registers - another INVALID cases of get registers.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) @@ -85,6 +93,7 @@ def test_shadowreg_get_reg_invalid(data_dir): shadowregs.get_register("REG1_Invalid") def test_shadowreg_invalid_probe(data_dir): + """Test Shadow Registers - INVALID probe used for constructor.""" probe = None config = get_config(os.path.join(data_dir, TEST_DATABASE)) @@ -96,7 +105,9 @@ def test_shadowreg_invalid_probe(data_dir): with pytest.raises(DP.DebugProbeError): shadowregs.get_register("REG1") +# pylint: disable=protected-access def test_shadowreg_verify_write(data_dir): + """Test Shadow Registers - Verify write to register test.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) @@ -108,31 +119,20 @@ def test_shadowreg_verify_write(data_dir): assert probe.mem_reg_read(1) == 0x87654321 probe.set_virtual_memory_substitute_data({1: [0x12345678, 0x5555AAAA]}) - with pytest.raises(SR.IoVerificationError): shadowregs._write_shadow_reg(1, 0x87654321, verify=True) assert probe.mem_reg_read(1) == 0x5555AAAA -def test_shadowreg_reverse(): - test_val = b'\x01\x02\x03\x04\x11\x12\x13\x14\x21\x22\x23\x24\x31\x32\x33\x34' - test_val_ret = b'\x04\x03\x02\x01\x14\x13\x12\x11\x24\x23\x22\x21\x34\x33\x32\x31' - - assert SR.ShadowRegisters._reverse_bytes_in_longs(test_val) == test_val_ret - assert SR.ShadowRegisters._reverse_bytes_in_longs(test_val_ret) == test_val - - test_val1 = b'\x01\x02\x03\x04\x11\x12' - with pytest.raises(ValueError): - SR.ShadowRegisters._reverse_bytes_in_longs(test_val1) - def test_shadowreg_yml(data_dir, tmpdir): + """Test Shadow Registers - Load YML configuration test.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) test_val = bytearray(32) - for i, val in enumerate(test_val): + for i in range(32): test_val[i] = i shadowregs.set_register("REG1", 0x12345678) @@ -187,11 +187,12 @@ def test_shadowreg_yml(data_dir, tmpdir): assert shadowregs_load2.get_register("REG_BIG_REV") == test_val def test_shadowreg_yml_corrupted(data_dir): + """Test Shadow Registers - Corrupted YML configuration.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) test_val = bytearray(32) - for i, val in enumerate(test_val): + for i in range(32): test_val[i] = i shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) @@ -204,13 +205,14 @@ def test_shadowreg_yml_corrupted(data_dir): assert shadowregs.get_register("REG_BIG_REV") == test_val def test_shadowreg_yml_invalid_computed(tmpdir, data_dir): + """Test Shadow Registers - INVALID computed configuration.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE_BAD_COMPUTED_FUNC)) shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) test_val = bytearray(32) - for i, val in enumerate(test_val): + for i in range(32): test_val[i] = i shadowregs.set_register("REG1", 0x12345678) @@ -233,12 +235,12 @@ def test_shadowreg_yml_invalid_computed(tmpdir, data_dir): shadowregs1.load_yml_config(os.path.join(tmpdir, "sh_regs.yml")) def test_shadowreg_yml_none_existing(data_dir): - + """Test Shadow Registers - None existing YML configuration.""" probe = get_probe() config = get_config(os.path.join(data_dir, TEST_DATABASE)) test_val = bytearray(32) - for i, val in enumerate(test_val): + for i in range(32): test_val[i] = i shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) @@ -246,38 +248,44 @@ def test_shadowreg_yml_none_existing(data_dir): shadowregs.load_yml_config(os.path.join(data_dir, "sh_regs_none.yml"), raw=True) def test_shadow_register_crc8(): + """Test Shadow Registers - CRC8 algorithm test.""" crc = SR.ShadowRegisters.crc_update(b'\x12\x34', is_final=False) crc = SR.ShadowRegisters.crc_update(b'\x56', crc=crc) assert crc == 0x29 def test_shadow_register_crc8_hook(): - bval = SR.value_to_bytes(0x03020100) - assert SR.ShadowRegisters.comalg_dcfg_cc_socu_crc8(SR.ShadowRegisters, bval) == b'\x03\x02\x01\x1d' + """Test Shadow Registers - CRC8 algorithm hook test.""" + bytes_value = SR.value_to_bytes(0x03020100) + assert SR.ShadowRegisters.comalg_dcfg_cc_socu_crc8(bytes_value) == b'\x03\x02\x01\x1d' - bval = SR.value_to_bytes(0x80FFFF00) - assert SR.ShadowRegisters.comalg_dcfg_cc_socu_crc8(SR.ShadowRegisters, bval) == SR.value_to_bytes(0x80FFFF20) + bytes_value = SR.value_to_bytes(0x80FFFF00) + assert SR.ShadowRegisters.comalg_dcfg_cc_socu_crc8(bytes_value) == SR.value_to_bytes(0x80FFFF20) def test_shadow_register_enable_debug_invalid_probe(): + """Test Shadow Registers - Enable debug algorithm check with invalid probe.""" probe = None with pytest.raises(SPSDKError): SR.enable_debug(probe) def test_shadow_register_enable_debug_device_cannot_enable(): + """Test Shadow Registers - Enable debug algorithm without connected target.""" probe = get_probe() - # invalid run (the mcu returns nonse values) + # invalid run (the mcu returns none sense values) assert not SR.enable_debug(probe) def test_shadow_register_enable_debug(): + """Test Shadow Registers - Enable debug algorithm check with valid target.""" probe = get_probe() #valid run, the right values are prepared #Setup the simulated data for reading of AP registers - ap = {12:["Exception", 0x12345678],0x02000000:[2, 0, 2, 0], 0x02000008:[0]} - probe.set_coresight_ap_substitute_data(ap) + access_port = {12:["Exception", 0x12345678], 0x02000000:[2, 0, 2, 0], 0x02000008:[0]} + probe.set_coresight_ap_substitute_data(access_port) assert SR.enable_debug(probe) def test_shadow_register_enable_debug_already_enabled(): + """Test Shadow Registers - Enable debug algorithm check with already enabled target.""" probe = get_probe() #Setup the simulated data for reading of AP registers mem_ap = {12:[0x12345678]} @@ -286,6 +294,7 @@ def test_shadow_register_enable_debug_already_enabled(): def test_shadow_register_enable_debug_probe_exceptions(): + """Test Shadow Registers - Enable debug algorithm check with probe exception.""" probe = get_probe() with pytest.raises(SPSDKError): probe.dp_write_cause_exception() diff --git a/tests/shadowregs/test_shadowregs_app.py b/tests/shadowregs/test_shadowregs_app.py index f1c0ecc3..68487f3a 100644 --- a/tests/shadowregs/test_shadowregs_app.py +++ b/tests/shadowregs/test_shadowregs_app.py @@ -4,7 +4,7 @@ # Copyright 2021 NXP # # SPDX-License-Identifier: BSD-3-Clause -"""Tests for nxpkeygen utility.""" +"""Tests for shadow registers utility.""" import os from click.testing import CliRunner @@ -163,7 +163,8 @@ def test_command_line_interface_setreg_exe(): """Test for printregs execution menu options.""" runner = CliRunner() enable_debug = '-o subs_ap={"12":["Exception",12345678],"33554432":[2,0,2,0],"33554440":[0]}' - cmd = f'-dev imxrt595 -i virtual -s {DebugProbeVirtual.UNIQUE_SERIAL} {enable_debug} setreg -r DCFG_CC_SOCU -v 12345678' + cmd = f'-dev imxrt595 -i virtual -s {DebugProbeVirtual.UNIQUE_SERIAL} {enable_debug}' + cmd += f' setreg -r DCFG_CC_SOCU -v 12345678' result = runner.invoke(main, cmd.split()) assert result.exit_code == 0 @@ -273,7 +274,8 @@ def test_command_line_interface_setreg_exe_fail(): runner = CliRunner() enable_debug = '-o subs_ap={"12":["Exception",12345678],"33554432":[2,0,2,0],"33554440":[0]} '\ '-o subs_mem={"1074987040":["Exception"]}' - cmd = f'-dev imxrt595 -i virtual -s {DebugProbeVirtual.UNIQUE_SERIAL} {enable_debug} setreg -r CUST_WR_RD_LOCK0 -v 12345678' + cmd = f'-dev imxrt595 -i virtual -s {DebugProbeVirtual.UNIQUE_SERIAL} {enable_debug}' + cmd += f' setreg -r CUST_WR_RD_LOCK0 -v 12345678' result = runner.invoke(main, cmd.split()) assert result.exit_code == 1 @@ -287,3 +289,12 @@ def test_command_line_interface_getreg_exe_fail(): result = runner.invoke(main, cmd.split()) assert result.exit_code == 1 + +def test_command_line_interface_generate_html(tmpdir): + """Test for info execution menu options.""" + runner = CliRunner() + cmd = f'-dev imxrt595 info -o {tmpdir}/imxrt_info.html' + result = runner.invoke(main, cmd.split()) + + assert result.exit_code == 0, result.output + assert os.path.isfile(f'{tmpdir}/imxrt_info.html') diff --git a/tests/utils/crypto/test_common.py b/tests/utils/crypto/test_common.py index 22e647c3..87ef79b8 100644 --- a/tests/utils/crypto/test_common.py +++ b/tests/utils/crypto/test_common.py @@ -1,14 +1,18 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause +""" Test of common crypto utilities module.""" +from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1 from spsdk.utils.crypto import Counter +from spsdk.utils.crypto.common import ecc_public_numbers_to_bytes, EllipticCurvePublicNumbers def test_counter(): + """Test of Counter.""" # simple counter with nonce only cntr = Counter(bytes([0] * 16)) assert cntr.value == bytes([0] * 16) @@ -32,3 +36,9 @@ def test_counter(): assert cntr.value == bytes([0] * 15 + [4]) cntr.increment(256) assert cntr.value == bytes([0] * 14 + [1, 4]) + +def test_ecc_public_numbers_to_bytes(): + """Test conversion ECC public numbers to bytes.""" + ecc = EllipticCurvePublicNumbers(0x1234567890ABCDEF, 0xEFCDAB9078563412, SECP256R1()) + assert ecc_public_numbers_to_bytes(ecc) == b'\x12\x34\x56\x78\x90\xab\xcd\xef\xef\xcd\xab\x90\x78\x56\x34\x12' + assert ecc_public_numbers_to_bytes(ecc, 8) == b'\x12\x34\x56\x78\x90\xab\xcd\xef\xef\xcd\xab\x90\x78\x56\x34\x12' diff --git a/tests/utils/data/bad_format.xml b/tests/utils/data/bad_format.xml new file mode 100644 index 00000000..9217def9 --- /dev/null +++ b/tests/utils/data/bad_format.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/utils/data/group_none_reg.yml b/tests/utils/data/group_none_reg.yml new file mode 100644 index 00000000..25e59f28 --- /dev/null +++ b/tests/utils/data/group_none_reg.yml @@ -0,0 +1,12 @@ +TestRegA0: +# Reg Description: TestRegA0 Description + value: '0x01020304' # The value width: 32b +TestRegA1: +# Reg Description: TestRegA1 Description + value: '0x11121314' # The value width: 32b +TestRegA2: +# Reg Description: TestRegA2 Description + value: '0x21222324' # The value width: 32b +TestRegA3: +# Reg Description: TestRegA3 Description + value: '0x31323334' # The value width: 32b \ No newline at end of file diff --git a/tests/utils/data/group_reg.yml b/tests/utils/data/group_reg.yml new file mode 100644 index 00000000..6600340e --- /dev/null +++ b/tests/utils/data/group_reg.yml @@ -0,0 +1,3 @@ +TestRegA: +# Reg Description: Group of TestRegA registers. + value: '0x01020304111213142122232431323334' # The value width: 128b diff --git a/tests/utils/data/grp_regs.xml b/tests/utils/data/grp_regs.xml new file mode 100644 index 00000000..168cb76a --- /dev/null +++ b/tests/utils/data/grp_regs.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/shadowregs/data/reg_config.json b/tests/utils/data/reg_config.json similarity index 59% rename from tests/shadowregs/data/reg_config.json rename to tests/utils/data/reg_config.json index 192edfda..7943a8b8 100644 --- a/tests/shadowregs/data/reg_config.json +++ b/tests/utils/data/reg_config.json @@ -10,10 +10,24 @@ "inverted_regs": { "INVERTED_REG": "INVERTED_REG_AP" }, + "computed_registers": [ + "COMPUTED_REG" + ], "computed_fields": { "COMPUTED_REG": {"TEST_FIELD1": "computed_reg_test_field1", "TEST_FIELD2": "computed_reg_test_field2"}, "COMPUTED_REG2": {"TEST_FIELD1": "computed_reg2_test_field1", "TEST_FIELD2": "computed_reg2_test_field2"} - } + }, + "ignored_registers": [ + "IGNORED_REG" + ], + "ignored_fields": [ + "FIELD" + ], + "grouped_registers": [ + {"name": "DeviceTest"} + ], + "seal_start": "COMPUTED_REG", + "seal_count": 4 }, "test_device2": { "revisions": { @@ -29,5 +43,22 @@ "COMPUTED_REG2": {"TEST_FIELD1": "computed_reg2_test_field1", "TEST_FIELD2": "computed_reg2_test_field2"} } } - } + }, + "inverted_regs": { + "INVERTED_REG": "INVERTED_REG_AP" + }, + "computed_registers": [ + "COMPUTED_REG_GENERAL" + ], + "ignored_registers": [ + "IGNORED_REG_GENERAL" + ], + "ignored_fields": [ + "FIELD_GENERAL" + ], + "grouped_registers": [ + {"name": "Test"} + ], + "seal_start": "COMPUTED_REG2", + "seal_count": 8 } diff --git a/tests/shadowregs/data/registers.xml b/tests/utils/data/registers.xml similarity index 98% rename from tests/shadowregs/data/registers.xml rename to tests/utils/data/registers.xml index c72c16b2..459a664b 100644 --- a/tests/shadowregs/data/registers.xml +++ b/tests/utils/data/registers.xml @@ -1,7 +1,7 @@ - + diff --git a/tests/shadowregs/data/registers_corr.xml b/tests/utils/data/registers_corr.xml similarity index 98% rename from tests/shadowregs/data/registers_corr.xml rename to tests/utils/data/registers_corr.xml index 130a5e7e..e837897e 100644 --- a/tests/shadowregs/data/registers_corr.xml +++ b/tests/utils/data/registers_corr.xml @@ -1,7 +1,7 @@ - + diff --git a/tests/utils/test_misc.py b/tests/utils/test_misc.py index 0a6a9592..47aa3efc 100644 --- a/tests/utils/test_misc.py +++ b/tests/utils/test_misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause import filecmp @@ -13,7 +13,9 @@ from spsdk.utils.misc import ( align, align_block, align_block_fill_random, extend_block, find_first, - load_binary, load_file, write_file) + load_binary, load_file, write_file, format_value, reverse_bytes_in_longs, + get_bytes_cnt_of_int, change_endianism, value_to_int, value_to_bytes, + value_to_bool) @pytest.mark.parametrize( @@ -189,3 +191,155 @@ def test_write_file(data_dir, tmpdir): assert filecmp.cmp(os.path.join(data_dir, 'file.bin'), os.path.join(tmpdir, 'file.bin')) assert filecmp.cmp(os.path.join(data_dir, 'file.txt'), os.path.join(tmpdir, 'file.txt')) + +@pytest.mark.parametrize( + "value,size,expected", + [ + (0, 2, "0b00"), (0, 4, "0b0000"), (0, 10, "0b00_0000_0000"), + (0, 8, "0x00"), (0, 16, "0x0000"), + (0, 32, "0x0000_0000"), + (0, 64, "0x0000_0000_0000_0000") + ] +) +def test_format_value(value, size, expected): + assert format_value(value, size) == expected + +def test_reg_long_reverse(): + """Test Register Config - reverse_bytes_in_longs function.""" + test_val = b'\x01\x02\x03\x04\x11\x12\x13\x14\x21\x22\x23\x24\x31\x32\x33\x34' + test_val_ret = b'\x04\x03\x02\x01\x14\x13\x12\x11\x24\x23\x22\x21\x34\x33\x32\x31' + + assert reverse_bytes_in_longs(test_val) == test_val_ret + assert reverse_bytes_in_longs(test_val_ret) == test_val + + test_val1 = b'\x01\x02\x03\x04\x11\x12' + with pytest.raises(ValueError): + reverse_bytes_in_longs(test_val1) + +@pytest.mark.parametrize( + "num, output, align_2_2n", + [ + (0, 1, True), + (1, 1, True), + ((1<<8)-1, 1, True), + ((1<<8), 2, True), + ((1<<16)-1, 2, True), + ((1<<16), 4, True), + ((1<<24)-1, 4, True), + ((1<<24), 4, True), + ((1<<32)-1, 4, True), + ((1<<32), 8, True), + ((1<<64)-1, 8, True), + ((1<<64), 12, True), + ((1<<128)-1, 16, True), + ((1<<128), 20, True), + (0, 1, False), + (1, 1, False), + ((1<<8)-1, 1, False), + ((1<<8), 2, False), + ((1<<16)-1, 2, False), + ((1<<16), 3, False), + ((1<<24)-1, 3, False), + ((1<<24), 4, False), + ((1<<32)-1, 4, False), + ((1<<32), 5, False), + ((1<<64)-1, 8, False), + ((1<<64), 9, False), + ((1<<128)-1, 16, False), + ((1<<128), 17, False), + ] +) +def test_get_bytes_cnt(num, output, align_2_2n): + """Test of get_bytes_cnt_of_int function.""" + assert output == get_bytes_cnt_of_int(num, align_2_2n) + +@pytest.mark.parametrize( + "value, res, exc", + [ + (b'\x12', b'\x12', False), + (b'\x12\x34', b'\x34\x12', False), + (b'\x12\x34\x56', b'\x56\x34\x12', True), + (b'\x12\x34\x56\x78', b'\x78\x56\x34\x12', False), + (b'\x12\x34\x56\x78\x12\x34\x56\x78', b'\x78\x56\x34\x12\x78\x56\x34\x12', False), + (b'\x12\x34\x56\x78\x12\x34\x56', b'\x78\x56\x34\x12\x78\x56\x34', True), + ] +) +def test_change_endianism(value, res, exc): + """Test of change_endianism function""" + if not exc: + assert res == change_endianism(value) + else: + with pytest.raises(ValueError): + change_endianism(value) + +@pytest.mark.parametrize( + "value, res, exc", + [ + (0, 0, False), + ("0", 0, False), + ("-1", -1, True), + ("0xffff", 65535, False), + ("ffff", 65535, False), + ("0xff_ff", 65535, False), + ("ff_ff", 65535, False), + ("0b111_1", 15, False), + ("b'111_1", 15, False), + (b'\xff\x00', 65280, False), + (bytearray(b'\xff\x00'), 65280, False), + ("InvalidValue", 0, True), + ] +) +def test_value_to_int(value, res, exc): + """Test of value_to_int function""" + if not exc: + assert res == value_to_int(value) + else: + with pytest.raises(TypeError): + value_to_int(value) + +@pytest.mark.parametrize( + "value, res, exc", + [ + (0, b'\x00', False), + ("0", b'\x00', False), + ("-1", b'\xff', True), + ("0xffff", b'\xff\xff', False), + ("ffff", b'\xff\xff', False), + ("0xff_ff", b'\xff\xff', False), + ("0b111_1", b'\x0f', False), + ("ff_ff", b'\xff\xff', False), + ("b'111_1", b'\x0f', False), + (b'\xff\x00', b'\xff\x00', False), + (bytearray(b'\xff\x00'), b'\xff\x00', False), + ("InvalidValue", 0, True), + ] +) +def test_value_to_bytes(value, res, exc): + """Test of value_to_bytes function""" + if not exc: + assert res == value_to_bytes(value) + else: + with pytest.raises(TypeError): + value_to_bytes(value) + + +@pytest.mark.parametrize( + "value, res, exc", + [ + (0, False, False), + (False, False, False), + ("False", False, False), + (1, True, False), + (True, True, False), + ("True", True, False), + ("T", True, False), + (b'\x20', True, True), + ] +) +def test_value_to_bool(value, res, exc): + """Test of value_to_bool function""" + if not exc: + assert res == value_to_bool(value) + else: + with pytest.raises(TypeError): + value_to_bool(value) diff --git a/tests/utils/test_registers.py b/tests/utils/test_registers.py new file mode 100644 index 00000000..7eeea040 --- /dev/null +++ b/tests/utils/test_registers.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +""" Tests for registers utility.""" + +import os +import filecmp +import pytest + +from spsdk.utils.exceptions import ( + SPSDKRegsError, SPSDKRegsErrorRegisterGroupMishmash, SPSDKRegsErrorRegisterNotFound, + SPSDKRegsErrorBitfieldNotFound, + SPSDKRegsErrorEnumNotFound +) +from spsdk.utils.registers import ( + Registers, + RegsRegister, + RegsBitField, + RegsEnum, +) + +from spsdk.utils.reg_config import RegConfig + +from spsdk.utils.misc import use_working_directory +from ruamel.yaml import YAML + +TEST_DEVICE_NAME = "TestDevice1" +TEST_REG_NAME = "TestReg" +TEST_REG_OFFSET = 1024 +TEST_REG_WIDTH = 32 +TEST_REG_DESCR = "TestReg Description" +TEST_REG_REV = False +TEST_REG_ACCESS = "RW" +TEST_REG_VALUE = 0xA5A5A5A5 + +TEST_BITFIELD_NAME = "TestBitfiled" +TEST_BITFILED_OFFSET = 0x0F +TEST_BITFILED_WIDTH = 5 +TEST_BITFIELD_RESET_VAL = 30 +TEST_BITFIELD_ACCESS = "RW" +TEST_BITFIELD_DESCR = "Test Bitfield Description" +TEST_BITFIELD_SAVEVAL = 29 +TEST_BITFIELD_OUTOFRANGEVAL = 70 + +TEST_ENUM_NAME = "TestEnum" +TEST_ENUM_VALUE_BIN = "0b10001" +TEST_ENUM_VALUE_HEX = "0x11" +TEST_ENUM_VALUE_STRINT = "017" +TEST_ENUM_VALUE_INT = 17 +TEST_ENUM_VALUE_BYTES = b'\x11' +TEST_ENUM_RES_VAL = "0b01_0001" +TEST_ENUM_DESCR = "Test Enum Description" +TEST_ENUM_MAXWIDTH = 6 + +TEST_XML_FILE = "unit_test.xml" + + +def test_basic_regs(tmpdir): + """Basic test of registers class.""" + regs = Registers(TEST_DEVICE_NAME) + + assert regs.dev_name == TEST_DEVICE_NAME + + reg1 = RegsRegister(TEST_REG_NAME, TEST_REG_OFFSET, TEST_REG_WIDTH, TEST_REG_DESCR, TEST_REG_REV, TEST_REG_ACCESS) + + with pytest.raises(SPSDKRegsErrorRegisterNotFound): + regs.find_reg("NonExisting") + + # The Registers MUST return empty array + assert regs.get_reg_names() == [] + + with pytest.raises(TypeError): + regs.remove_register("String") + + with pytest.raises(ValueError): + regs.remove_register(reg1) + + # Now we could do tests with a register added to list + regs.add_register(reg1) + + regs.remove_register_by_name(["String"]) + + assert TEST_REG_NAME in regs.get_reg_names() + + regt = regs.find_reg(TEST_REG_NAME) + + assert regt == reg1 + + with pytest.raises(TypeError): + regs.add_register("Invalid Parameter") + + regt.set_value(TEST_REG_VALUE) + assert reg1.get_value() == TEST_REG_VALUE.to_bytes(4, "big") + + filename = os.path.join(tmpdir, TEST_XML_FILE) + regs.write_xml(filename) + assert os.path.isfile(filename) + + printed_str = str(regs) + + assert TEST_DEVICE_NAME in printed_str + assert TEST_REG_NAME in printed_str + + regs.remove_register_by_name([TEST_REG_NAME]) + + with pytest.raises(SPSDKRegsErrorRegisterNotFound): + regs.find_reg(TEST_REG_NAME) + assert False + + +def test_register(): + """Basic registers test.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) + bitfield.add_enum(enum) + + parent_reg.add_bitfield(bitfield) + + printed_str = str(parent_reg) + + assert "Name:" in printed_str + assert TEST_REG_NAME in printed_str + assert TEST_REG_DESCR in printed_str + assert "Width:" in printed_str + assert "Access:" in printed_str + assert "Bitfield" in printed_str + assert TEST_BITFIELD_NAME in printed_str + assert TEST_BITFIELD_DESCR in printed_str + assert TEST_ENUM_NAME in printed_str + assert TEST_ENUM_DESCR in printed_str + + +def test_register_invalid_val(): + """Invalid value register test.""" + reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + val = reg.get_value() + reg.set_value("Invalid") + assert reg.get_value() == val + + reg.set_value([1, 2]) + assert reg.get_value() == val + + +def test_enum(): + """Basic Enum test.""" + enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) + + printed_str = str(enum) + + assert "Name:" in printed_str + assert "Value:" in printed_str + assert "Description:" in printed_str + assert TEST_ENUM_NAME in printed_str + assert "0x0" in printed_str + assert TEST_ENUM_DESCR in printed_str + + +def test_enum_bin(): + """Enum test with binary value.""" + enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_BIN, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert TEST_ENUM_RES_VAL in printed_str + + +def test_enum_hex(): + """Enum test with hexadecimal value.""" + enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_HEX, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert TEST_ENUM_RES_VAL in printed_str + + +def test_enum_strint(): + """Enum test with integer in string value.""" + enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_STRINT, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert TEST_ENUM_RES_VAL in printed_str + + +def test_enum_int(): + """Enum test with integer value.""" + enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_INT, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert TEST_ENUM_RES_VAL in printed_str + + +def test_enum_bytes(): + """Enum test with bytes array value.""" + enum = RegsEnum(TEST_ENUM_NAME, TEST_ENUM_VALUE_BYTES, TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert TEST_ENUM_RES_VAL in printed_str + + +def test_enum_invalidval(): + """Enum test with INVALID value.""" + try: + enum = RegsEnum(TEST_ENUM_NAME, "InvalidValue", TEST_ENUM_DESCR, TEST_ENUM_MAXWIDTH) + printed_str = str(enum) + assert "N/A" in printed_str + except TypeError: + assert 0 + + +def test_bitfield(): + """Basic bitfield test.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) + bitfield.add_enum(enum) + + parent_reg.add_bitfield(bitfield) + + printed_str = str(bitfield) + + assert "Name:" in printed_str + assert "Offset:" in printed_str + assert "Width:" in printed_str + assert "Access:" in printed_str + assert "Reset val:" in printed_str + assert "Description:" in printed_str + assert "Enum" in printed_str + + +def test_bitfield_find(): + """Test bitfield find function.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) + bitfield.add_enum(enum) + + parent_reg.add_bitfield(bitfield) + + assert bitfield == parent_reg.find_bitfield(TEST_BITFIELD_NAME) + + with pytest.raises(SPSDKRegsErrorBitfieldNotFound): + parent_reg.find_bitfield("Invalid Name") + + +def test_bitfields_names(): + """Test bitfield get names function.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + bitfield1 = RegsBitField(parent_reg, + TEST_BITFIELD_NAME+"1", + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + assert parent_reg.get_bitfield_names() == [] + + parent_reg.add_bitfield(bitfield) + parent_reg.add_bitfield(bitfield1) + + assert len(parent_reg.get_bitfield_names()) == 2 + + names = parent_reg.get_bitfield_names() + assert len(names) == 2 + assert TEST_BITFIELD_NAME in names + assert TEST_BITFIELD_NAME+"1" in names + + ex_names = parent_reg.get_bitfield_names([TEST_BITFIELD_NAME+"1"]) + assert len(ex_names) == 1 + assert TEST_BITFIELD_NAME in ex_names + + ex_names1 = parent_reg.get_bitfield_names([TEST_BITFIELD_NAME]) + assert len(ex_names1) == 0 + + +def test_bitfield_has_enums(): + """Test bitfield has enums function.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + parent_reg.add_bitfield(bitfield) + + assert bitfield.has_enums() is False + enum = RegsEnum(TEST_ENUM_NAME, 0, TEST_ENUM_DESCR) + bitfield.add_enum(enum) + + assert bitfield.has_enums() is True + + assert enum in bitfield.get_enums() + + +def test_bitfield_value(): + """Test bitfield functionality about values.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + bitfield.set_value(TEST_BITFIELD_SAVEVAL) + assert bitfield.get_value() == TEST_BITFIELD_SAVEVAL + + with pytest.raises(ValueError): + bitfield.set_value(TEST_BITFIELD_OUTOFRANGEVAL) + + +def test_bitfield_invalidvalue(): + """Test bitfield INVALID value.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + "InvalidValue", + TEST_BITFIELD_ACCESS) + + assert bitfield.get_value() == 0 + + +def test_bitfield_enums(): + """Test bitfield enums.""" + parent_reg = RegsRegister(TEST_REG_NAME, + TEST_REG_OFFSET, + TEST_REG_WIDTH, + TEST_REG_DESCR, + TEST_REG_REV, + TEST_REG_ACCESS) + + bitfield = RegsBitField(parent_reg, + TEST_BITFIELD_NAME, + TEST_BITFILED_OFFSET, + TEST_BITFILED_WIDTH, + TEST_BITFIELD_DESCR, + TEST_BITFIELD_RESET_VAL, + TEST_BITFIELD_ACCESS) + + parent_reg.add_bitfield(bitfield) + + enums = [] + for index in range((1 << TEST_BITFILED_WIDTH)-1): + enum = RegsEnum(f"{TEST_ENUM_NAME}{index}", index, f"{TEST_ENUM_DESCR}{index}", TEST_BITFILED_WIDTH) + enums.append(enum) + bitfield.add_enum(enum) + + enum_names = bitfield.get_enum_names() + + for index in range((1 << TEST_BITFILED_WIDTH)-1): + assert index == bitfield.get_enum_constant(f"{TEST_ENUM_NAME}{index}") + assert enums[index].name in enum_names + + for index in range((1 << TEST_BITFILED_WIDTH)): + bitfield.set_value(index) + if index < (1 << TEST_BITFILED_WIDTH)-1: + assert f"{TEST_ENUM_NAME}{index}" == bitfield.get_enum_value() + else: + assert index == bitfield.get_enum_value() + + for index in range((1 << TEST_BITFILED_WIDTH)-1): + bitfield.set_enum_value(f"{TEST_ENUM_NAME}{index}") + assert index == bitfield.get_value() + + with pytest.raises(SPSDKRegsErrorEnumNotFound): + bitfield.get_enum_constant("Invalid name") + + +def test_registers_xml(data_dir, tmpdir): + """Test registers XML support.""" + regs = Registers(TEST_DEVICE_NAME) + + with use_working_directory(data_dir): + regs.load_registers_from_xml("registers.xml") + + with use_working_directory(tmpdir): + regs.write_xml("registers.xml") + + regs2 = Registers(TEST_DEVICE_NAME) + + with use_working_directory(tmpdir): + regs2.load_registers_from_xml("registers.xml") + + assert str(regs) == str(regs2) + + +def test_registers_xml_bad_format(data_dir): + """Test registers XML support - BAd XML format exception.""" + regs = Registers(TEST_DEVICE_NAME) + + with pytest.raises(SPSDKRegsError): + regs.load_registers_from_xml(data_dir+"/bad_format.xml") + + +def test_registers_corrupted_xml(data_dir, tmpdir): + """Test registers XML support with invalid data.""" + regs = Registers(TEST_DEVICE_NAME) + + with use_working_directory(data_dir): + regs.load_registers_from_xml("registers_corr.xml") + + with use_working_directory(tmpdir): + regs.write_xml("registers_corr.xml") + + assert not filecmp.cmp(os.path.join(data_dir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr.xml")) + + regs.clear() + + with use_working_directory(tmpdir): + regs.load_registers_from_xml("registers_corr.xml") + regs.write_xml("registers_corr1.xml") + + assert filecmp.cmp(os.path.join(tmpdir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr1.xml")) + + # Without clear - Cannot add register with same name as is already added + with use_working_directory(tmpdir): + regs.load_registers_from_xml("registers_corr.xml") + regs.write_xml("registers_corr1.xml") + + assert filecmp.cmp(os.path.join(tmpdir, "registers_corr.xml"), os.path.join(tmpdir, "registers_corr1.xml")) + + +def test_reg_config_get_devices(data_dir): + """Test Register Config - get_devices function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + devices = reg_config.get_devices() + + assert "test_device1" in devices + assert "test_device2" in devices + + +def test_reg_config_get_devices_class(data_dir): + """Test Register Config - get_devices class function.""" + devices = RegConfig.devices(os.path.join(data_dir, "reg_config.json")) + + assert "test_device1" in devices + assert "test_device2" in devices + + +def test_reg_config_get_latest_revision(data_dir): + """Test Register Config - get_latest_revision function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + rev = reg_config.get_latest_revision("test_device1") + assert rev == "x1" + + rev = reg_config.get_latest_revision("test_device2") + assert rev == "b0" + + +def test_reg_config_get_revisions(data_dir): + """Test Register Config - get_revisions function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + revs = reg_config.get_revisions("test_device1") + assert "x0" in revs + assert "x1" in revs + + revs = reg_config.get_revisions("test_device2") + assert "b0" in revs + + +def test_reg_config_get_address(data_dir): + """Test Register Config - get_address function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + addr = reg_config.get_address("test_device1") + assert addr == "0xA5A5_1234" + + addr = reg_config.get_address("test_device2", remove_underscore=True) + assert addr == "0x40000000" + + +def test_reg_config_get_data_file(data_dir): + """Test Register Config - get_data_file function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + data_file = reg_config.get_data_file("test_device1", "x0") + assert os.path.join(data_dir, "test_device1_x0.xml") == data_file + + data_file = reg_config.get_data_file("test_device1", "x1") + assert os.path.join(data_dir, "test_device1_x1.xml") == data_file + + data_file = reg_config.get_data_file("test_device2", "b0") + assert os.path.join(data_dir, "test_device2_b0.xml") == data_file + + +def test_reg_config_get_antipole_regs(data_dir): + """Test Register Config - get_antipole_regs function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + antipole = reg_config.get_antipole_regs("test_device1") + assert antipole["INVERTED_REG"] == "INVERTED_REG_AP" + + antipole = reg_config.get_antipole_regs("test_device2") + assert antipole["INVERTED_REG"] == "INVERTED_REG_AP" + + antipole = reg_config.get_antipole_regs() + assert antipole["INVERTED_REG"] == "INVERTED_REG_AP" + + +def test_reg_config_get_computed_regs(data_dir): + """Test Register Config - get_computed_registers function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + computed_regs = reg_config.get_computed_registers("test_device1") + assert "COMPUTED_REG" in computed_regs + + computed_regs = reg_config.get_computed_registers("test_device2") + assert "COMPUTED_REG_GENERAL" in computed_regs + + computed_regs = reg_config.get_computed_registers("invalid_device") + assert "COMPUTED_REG_GENERAL" in computed_regs + + computed_regs = reg_config.get_computed_registers() + assert "COMPUTED_REG_GENERAL" in computed_regs + + +def test_reg_config_get_seal_start_address(data_dir): + """Test Register Config - get_seal_start_address function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + seal_address = reg_config.get_seal_start_address("test_device1") + assert seal_address == "COMPUTED_REG" + + seal_address = reg_config.get_seal_start_address("test_device2") + assert seal_address == "COMPUTED_REG2" + + seal_address = reg_config.get_seal_start_address("invalid_device") + assert seal_address == "COMPUTED_REG2" + + seal_address = reg_config.get_seal_start_address() + assert seal_address == "COMPUTED_REG2" + + +def test_reg_config_get_seal_count(data_dir): + """Test Register Config - get_seal_count function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + seal_count = reg_config.get_seal_count("test_device1") + assert seal_count == 4 + + seal_count = reg_config.get_seal_count("test_device2") + assert seal_count == 8 + + seal_count = reg_config.get_seal_count("invalid_device") + assert seal_count == 8 + + seal_count = reg_config.get_seal_count() + assert seal_count == 8 + + +def test_reg_config_get_ignored_registers(data_dir): + """Test Register Config - get_ignored_registers function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + ignored_registers = reg_config.get_ignored_registers("test_device1") + assert "IGNORED_REG" in ignored_registers + + ignored_registers = reg_config.get_ignored_registers("test_device2") + assert "IGNORED_REG_GENERAL" in ignored_registers + + ignored_registers = reg_config.get_ignored_registers("invalid_device") + assert "IGNORED_REG_GENERAL" in ignored_registers + + ignored_registers = reg_config.get_ignored_registers() + assert "IGNORED_REG_GENERAL" in ignored_registers + + +def test_reg_config_get_ignored_fields(data_dir): + """Test Register Config - get_ignored_fields function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + ignored_fields = reg_config.get_ignored_fields("test_device1") + assert "FIELD" in ignored_fields + + ignored_fields = reg_config.get_ignored_fields("test_device2") + assert "FIELD_GENERAL" in ignored_fields + + ignored_fields = reg_config.get_ignored_fields("invalid_device") + assert "FIELD_GENERAL" in ignored_fields + + ignored_fields = reg_config.get_ignored_fields() + assert "FIELD_GENERAL" in ignored_fields + + +def test_reg_config_get_computed_fields(data_dir): + """Test Register Config - get_computed_fields function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + computed_fields = reg_config.get_computed_fields("test_device1") + assert computed_fields["COMPUTED_REG"]["TEST_FIELD1"] == "computed_reg_test_field1" + assert computed_fields["COMPUTED_REG"]["TEST_FIELD2"] == "computed_reg_test_field2" + assert computed_fields["COMPUTED_REG2"]["TEST_FIELD1"] == "computed_reg2_test_field1" + assert computed_fields["COMPUTED_REG2"]["TEST_FIELD2"] == "computed_reg2_test_field2" + + +def test_reg_config_get_grouped_registers(data_dir): + """Test Register Config - get_grouped_registers function.""" + reg_config = RegConfig(os.path.join(data_dir, "reg_config.json")) + + grouped_registers = reg_config.get_grouped_registers("test_device1") + assert grouped_registers[0]["name"] == "DeviceTest" + grouped_registers = reg_config.get_grouped_registers("test_device2") + assert grouped_registers[0]["name"] == "Test" + + +def test_basic_grouped_register(data_dir): + """Test basic functionality of register grouping functionality""" + regs = Registers(TEST_DEVICE_NAME) + + group = [ + { + "name": "TestRegA" + } + ] + + regs.load_registers_from_xml(data_dir + "/grp_regs.xml", grouped_regs=group) + + reg = regs.find_reg("TestRegA") + assert reg.offset == 0x400 + assert reg.width == 4*32 + + reg.set_value("01020304_11121314_21222324_31323334") + assert regs.find_reg("TestRegA0", include_group_regs=True).get_hex_value() == "0x01020304" + assert regs.find_reg("TestRegA1", include_group_regs=True).get_hex_value() == "0x11121314" + assert regs.find_reg("TestRegA2", include_group_regs=True).get_hex_value() == "0x21222324" + assert regs.find_reg("TestRegA3", include_group_regs=True).get_hex_value() == "0x31323334" + assert regs.find_reg("TestRegA0", include_group_regs=True).reverse == False + assert regs.find_reg("TestRegA1", include_group_regs=True).reverse == False + assert regs.find_reg("TestRegA2", include_group_regs=True).reverse == False + assert regs.find_reg("TestRegA3", include_group_regs=True).reverse == False + assert reg.get_hex_value() == "01020304111213142122232431323334" + + +def test_basic_grouped_register_reversed_value(data_dir): + """Test basic functionality of register grouping functionality with reversed value""" + regs = Registers(TEST_DEVICE_NAME) + + group = [ + { + "name": "TestRegA", + "reverse": "True" + } + ] + + regs.load_registers_from_xml(data_dir + "/grp_regs.xml", grouped_regs=group) + + reg = regs.find_reg("TestRegA") + assert reg.offset == 0x400 + assert reg.width == 4*32 + + reg.set_value("0x01020304_11121314_21222324_31323334") + assert regs.find_reg("TestRegA0", include_group_regs=True).get_hex_value() == "0x01020304" + assert regs.find_reg("TestRegA1", include_group_regs=True).get_hex_value() == "0x11121314" + assert regs.find_reg("TestRegA2", include_group_regs=True).get_hex_value() == "0x21222324" + assert regs.find_reg("TestRegA3", include_group_regs=True).get_hex_value() == "0x31323334" + assert regs.find_reg("TestRegA0", include_group_regs=True).reverse == True + assert regs.find_reg("TestRegA1", include_group_regs=True).reverse == True + assert regs.find_reg("TestRegA2", include_group_regs=True).reverse == True + assert regs.find_reg("TestRegA3", include_group_regs=True).reverse == True + + assert reg.get_hex_value() == "01020304111213142122232431323334" + + +@pytest.mark.parametrize( + "group_reg", + [ + [{"name": "TestCorrupted0Reg"}], + [{"name": "TestRegA", "width": 96}], + [{"name": "TestRegA", "offset": 0x410}], + [{"name": "TestCorrupted1Reg"}], + [{"name": "TestCorrupted1Reg", "width": 64}], + [{"name": "TestRegA", "access": "R"}], + [{"name": "TestCorrupted2Reg", "width": 32}] + ] +) +def test_grouped_register_invalid_params(data_dir, group_reg): + """Test of register grouping with invalid width""" + regs = Registers(TEST_DEVICE_NAME) + + with pytest.raises(SPSDKRegsErrorRegisterGroupMishmash): + regs.load_registers_from_xml(data_dir + "/grp_regs.xml", grouped_regs=group_reg) + + +def test_load_grouped_register_value(data_dir): + """Simply test to handle load of individual registers into grouped from YML.""" + regs = Registers(TEST_DEVICE_NAME) + + group = [ + { + "name": "TestRegA" + } + ] + regs.load_registers_from_xml(data_dir + "/grp_regs.xml", grouped_regs=group) + yaml = YAML() + with open(data_dir+"/group_reg.yml", "r") as yml_file: + data = yaml.load(yml_file) + regs.load_yml_config(data) + reg = regs.find_reg("TestRegA") + assert reg.get_hex_value() == "01020304111213142122232431323334" + assert regs.find_reg("TestRegA0", include_group_regs=True).get_hex_value() == "0x01020304" + assert regs.find_reg("TestRegA1", include_group_regs=True).get_hex_value() == "0x11121314" + assert regs.find_reg("TestRegA2", include_group_regs=True).get_hex_value() == "0x21222324" + assert regs.find_reg("TestRegA3", include_group_regs=True).get_hex_value() == "0x31323334" + + +def test_load_grouped_register_value_compatibility(data_dir): + """Simply test to handle load of individual registers into grouped from YML.""" + regs = Registers(TEST_DEVICE_NAME) + + group = [ + { + "name": "TestRegA" + } + ] + regs.load_registers_from_xml(data_dir + "/grp_regs.xml", grouped_regs=group) + yaml = YAML() + with open(data_dir+"/group_none_reg.yml", "r") as yml_file: + data = yaml.load(yml_file) + regs.load_yml_config(data) + reg = regs.find_reg("TestRegA") + assert reg.get_hex_value() == "01020304111213142122232431323334" + assert regs.find_reg("TestRegA0", include_group_regs=True).get_hex_value() == "0x01020304" + assert regs.find_reg("TestRegA1", include_group_regs=True).get_hex_value() == "0x11121314" + assert regs.find_reg("TestRegA2", include_group_regs=True).get_hex_value() == "0x21222324" + assert regs.find_reg("TestRegA3", include_group_regs=True).get_hex_value() == "0x31323334" diff --git a/tools/gitcov-defaults.ini b/tools/gitcov-defaults.ini index e29daf3f..5fcb3eb2 100644 --- a/tools/gitcov-defaults.ini +++ b/tools/gitcov-defaults.ini @@ -3,7 +3,8 @@ skip-files = sdp/interfaces, mboot/interfaces, spsdk/apps, - spsdk/dat, + spsdk/dat/dm_commands.py, + spsdk/dat/debug_mailbox.py, spsdk/debuggers/debug_probe_jlink.py, spsdk/debuggers/debug_probe_pemicro.py, spsdk/debuggers/debug_probe_pyocd.py, @@ -15,5 +16,6 @@ coverage-report=reports/coverage.xml coverage-cutoff=0.8 parent-branch=origin/master include-merges=0 -verbose=1 +verbose=0 debug=0 +full-file-test=0 diff --git a/tools/gitcov.py b/tools/gitcov.py index a5494d08..c279b792 100644 --- a/tools/gitcov.py +++ b/tools/gitcov.py @@ -17,6 +17,11 @@ from typing import Sequence, Tuple from xml.etree import ElementTree as et +logger = logging.getLogger() +# Modify logger to proper format +LOG_HANDLER = logging.StreamHandler() +LOG_HANDLER.setFormatter(logging.Formatter('%(message)s')) +logger.addHandler(LOG_HANDLER) class MyFormatter( argparse.ArgumentDefaultsHelpFormatter, @@ -24,7 +29,6 @@ class MyFormatter( ): """Class customizing behavior for argparse.""" - def parse_input(input_args: Sequence[str] = None) -> argparse.Namespace: """Parse default configuration file and process user inputs.""" # read the gitcov-defaults.ini use values to set defaults to argparse @@ -71,6 +75,10 @@ def parse_input(input_args: Sequence[str] = None) -> argparse.Namespace: "-d", "--debug", default=config.BOOLEAN_STATES[gitcov_config["debug"]], required=False, action='store_true', help="Debugging output" ) + parser.add_argument( + "-f", "--full-file-test", default=config.BOOLEAN_STATES[gitcov_config["full-file-test"]], + required=False, action='store_true', help="Enable full file test instead of branch changes" + ) parser.add_argument( "-c", "--config-file", required=False, help=("""Path to config .ini file. @@ -94,7 +102,7 @@ def parse_input(input_args: Sequence[str] = None) -> argparse.Namespace: log_level = logging.INFO if gitcov_config.getint("debug", fallback=0) or args.debug: log_level = logging.DEBUG - logging.basicConfig(level=log_level) + args.log_level = log_level assert path.isdir(args.repo_path), f"Repo path '{args.repo_path}' doesn't exist" args.repo_path = path.abspath(args.repo_path) @@ -107,7 +115,7 @@ def parse_input(input_args: Sequence[str] = None) -> argparse.Namespace: return args -def get_changed_files(repo_path: str, parent_branch: str, include_merges: bool) -> Sequence[str]: +def get_changed_files(repo_path: str, include_merges: bool, parent_branch: str = None) -> Sequence[str]: """Get a list of changed files. :param repo_path: Path to the root of the repository @@ -118,27 +126,29 @@ def get_changed_files(repo_path: str, parent_branch: str, include_merges: bool) file_regex_str = r"^(?P[AM])\s+(?P[a-zA-Z0-9_/\\]+\.py)$" file_regex = re.compile(file_regex_str) + parent_branch = parent_branch or get_parent_commit() + # fetch changed files from previous commits - logging.info("Fetching files from previous commits\n") + logger.info("Fetching files from previous commits\n") cmd = f"git log {'' if include_merges else '--no-merges --first-parent'} --name-status {parent_branch}..HEAD" - logging.debug(f"Executing: {cmd}") + logger.debug(f"Executing: {cmd}") all_files = subprocess.check_output(cmd.split(), cwd=repo_path).decode("utf-8") - logging.debug(f"Result:\n{all_files}") + logger.debug(f"Result:\n{all_files}") # fetch changed files that are potentionally not committed yet - logging.info("Fetching uncommitted files\n") + logger.info("Fetching uncommitted files\n") cmd = f"git diff --name-status" - logging.debug(f"Executing: {cmd}") + logger.debug(f"Executing: {cmd}") uncommitted = subprocess.check_output(cmd.split(), cwd=repo_path).decode("utf-8") - logging.debug(f"Result:\n{uncommitted}") + logger.debug(f"Result:\n{uncommitted}") all_files += uncommitted # fetch staged new files - logging.info("Fetching new files... those need to be stagged\n") + logger.info("Fetching new files... those need to be stagged\n") cmd = f"git diff --name-status --cached" - logging.debug(f"Executing: {cmd}") + logger.debug(f"Executing: {cmd}") staged = subprocess.check_output(cmd.split(), cwd=repo_path).decode("utf-8") - logging.debug(f"Result:\n{staged}") + logger.debug(f"Result:\n{staged}") all_files += staged filtered = [] @@ -148,7 +158,7 @@ def get_changed_files(repo_path: str, parent_branch: str, include_merges: bool) filtered.append(match.group("path")) # remove duplicates filtered = list(set(filtered)) - logging.debug(f"Files to consider: {len(filtered)}: {filtered}") + logger.debug(f"Files to consider: {len(filtered)}: {filtered}") return list(set(filtered)) @@ -164,7 +174,7 @@ def extract_linenumber(base_dir: str, file_path: str, parent_branch: str) -> Seq line_regex = re.compile(line_regex_str) cmd = f"git diff {parent_branch} --unified=0 -- {file_path}" - logging.debug(f"Executing: {cmd}") + logger.debug(f"Executing: {cmd}") git_diff = subprocess.check_output(cmd.split(), cwd=base_dir).decode("utf-8") line_numbers = [] for line in git_diff.split("\n"): @@ -191,7 +201,7 @@ def _cov_branch_category(line: et.Element) -> str: return category -def extract_coverage(cov_report: et.ElementTree, file_path: str, line_numbers: Sequence[int]) -> dict: +def extract_coverage(cov_report: et.ElementTree, file_path: str, line_numbers: Sequence[int] = None) -> dict: """Extract coverage data for a given file. :param cov_report: Parsed xml coverage report @@ -203,13 +213,17 @@ def extract_coverage(cov_report: et.ElementTree, file_path: str, line_numbers: S data: dict = {"statement": {"hit": [], "miss": []}, "branch": {"hit": [], "miss": [], "partial": []}} for item in lines_elem: line_num = int(item.attrib["number"]) - if line_num not in line_numbers: + if line_numbers and line_num not in line_numbers: continue data["statement"][_cov_statement_category(item)].append(line_num) if "branch" in item.attrib: data["branch"][_cov_branch_category(item)].append(line_num) return data +def uncovered_changed_lines(statement_lines: list, branch_lines: list, changed_lines: list) -> set: + """Get the set of changed lines which are not covered.""" + all_bad_lines = set(statement_lines).union(set(branch_lines)) + return all_bad_lines.intersection(set(changed_lines)) def calc_statement_coverage(statement_data: dict) -> float: """Calculate result statement coverage.""" @@ -252,47 +266,145 @@ def is_skipped(file_path: str, skip_patterns: Sequence[str]) -> bool: """Find whether file should qualifies given filer patterns.""" return any(skip_pattern in file_path for skip_pattern in skip_patterns) +def get_parent_commit() -> str: + """Returns commit of parent branch. + + Iteratively looks at parent commits of current commit and checks, whether + the parent commit belongs to different branches. In case it does, this + should be the point where we branched off and the given commit is returned. + + An exception can be raised in case the repository is empty (fresh new repo) + or there are no branches. + + :return: sha of found commit + :raises: CalledProcessError + """ + # !!!Warning: This approcha will fail, if applied for branch B1 to get M!!! + # M ---A----B-----C + # B1 \---D--E + # B2 \---F--G + # + # With the above scenario, we want to identify changes between B2 and + # point we branched of - commit D. + # To achieve this, the process is following: + # + # 1. get the current sha: $ git rev-parse HEAD + # iterate is_crossroad: + # 2. get all branches the commit is part of: $ git branch -a --cotains {SHA} + # 3. check whether returned branches contain other branches except the one + # we are on (crossrad) + # 4. we haven't found a crossroad, get next sha: $ git rev-parse {SHA}^ + # + # Example: + # get commit G sha + # Check all branches G is part of + # G is only part of B2 + # Get next commit F sha + # Check all branches F is part of + # F is only part of B2 + # Get next commit D sha + # Check all branches D is part of + # D is part of B1 and B2 branches + # We are on a crossroad -> return D sha + current_sha = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip() + logging.debug(f"Initial sha: {current_sha}") + while 1: + + current_branches = subprocess.check_output(["git", "branch", "-a", "--contains", current_sha]) + + branches = list(filter(None, current_branches.decode('utf-8').split('\n'))) + logging.debug(f"All branches containing sha {current_sha}: {branches}") + + current_branch = subprocess.check_output(["git", "branch", "--show-current"]).strip() + logging.debug(f"We are on branch: {current_branch}") + + on_crossroad = False + for branch in branches: + if current_branch.decode('utf-8') not in branch: + on_crossroad = True + break + + if on_crossroad is True: + break + + current_sha = subprocess.check_output(["git", "rev-parse", current_sha + b'^']).strip() + logging.debug(f"Parent sha: {current_sha}") + + return current_sha def main(argv: Sequence[str] = None) -> int: """Main function.""" args = parse_input(argv) - logging.debug(args) + + default_log_level = args.log_level + logger.setLevel(default_log_level) + logger.handlers[0].setLevel(default_log_level) + + logger.debug(args) files = get_changed_files( repo_path=args.repo_path, parent_branch=args.parent_branch, include_merges=args.include_merges ) files = [f for f in files if f.startswith(args.module)] - logging.debug(f"files to process: {len(files)}: {files}\n") + logger.debug(f"files to process: {len(files)}: {files}\n") cov_report = et.parse(args.coverage_report) error_counter = 0 + for f in files: - logging.info(f"processing: {f}") is_skipped_file = is_skipped(f, args.skip_files) - if is_skipped_file: - logging.info("This file is skipped and will not contribute to the error counter.") - git_numbers = extract_linenumber(args.repo_path, f, args.parent_branch) - logging.debug(f"git lines: {git_numbers}") # the coverage.xml removes the module name from path sanitized_name = f.replace(f"{args.module}/", "") - cov_numbers = extract_coverage(cov_report, sanitized_name, git_numbers) - logging.debug(f"cov lines: {cov_numbers}") + cov_numbers = extract_coverage(cov_report, sanitized_name, None if args.full_file_test else git_numbers) statement_cov, branch_cov = calc_coverage(cov_numbers) - logging.info(f"uncovered lines: {cov_numbers['statement']['miss']}") + changed_uncovered_lines = uncovered_changed_lines( + cov_numbers['statement']['miss'], + cov_numbers['branch']['miss'], + git_numbers) + no_fails = statement_cov * branch_cov in (1.0, -1.0) + critical_fails = not no_fails and not is_skipped_file + if not did_pass(statement_cov, args.coverage_cutoff) and not is_skipped_file: error_counter += 1 - logging.info(f"uncovered branches: {cov_numbers['branch']['miss']}") - logging.info(f"partially covered branches: {cov_numbers['branch']['partial']}") if not did_pass(branch_cov, args.coverage_cutoff) and not is_skipped_file: error_counter += 1 - logging.info(f"Statement coverage: {stringify_pass(statement_cov, args.coverage_cutoff)}") - logging.info(f"Branch coverage: {stringify_pass(branch_cov, args.coverage_cutoff)}\n") + + # Change temporary, if needed, log level to print interesting information + if critical_fails and logger.level > logging.INFO: + logger.handlers[0].setLevel(logging.INFO) + logger.setLevel(logging.INFO) + + logger.info(f"processing: {f}") + + if is_skipped_file: + logger.info(f"The file is skipped and will not contribute to the error counter.") + if logger.level > logging.DEBUG: + logger.info("") # Just add a new line + continue + + logger.debug(f"git lines: {git_numbers}") + logger.debug(f"cov lines: {cov_numbers}") + + if no_fails: + logger.info(f"File is fully covered.\n") + else: + changed_uncovered_lines_msg = changed_uncovered_lines if changed_uncovered_lines != set() else "None" + logger.info(f"changed uncovered lines: {changed_uncovered_lines_msg}") + logger.info(f"uncovered lines: {cov_numbers['statement']['miss']}") + logger.info(f"uncovered branches: {cov_numbers['branch']['miss']}") + logger.info(f"partially covered branches: {cov_numbers['branch']['partial']}") + logger.info(f"Statement coverage: {stringify_pass(statement_cov, args.coverage_cutoff)}") + logger.info(f"Branch coverage: {stringify_pass(branch_cov, args.coverage_cutoff)}\n") + + # Return back log level to default value + logger.handlers[0].setLevel(default_log_level) + logger.setLevel(default_log_level) if error_counter == 0: - logging.info("No errors found") + logger.info("No errors found") else: - logging.error(f"Total errors: {error_counter}") + logger.error(f"Total errors: {error_counter}") return error_counter diff --git a/tools/sr_xls2xml.py b/tools/sr_xls2xml.py index 9b1296d4..6eaf96cc 100644 --- a/tools/sr_xls2xml.py +++ b/tools/sr_xls2xml.py @@ -1,13 +1,12 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021 NXP +# Copyright 2020-2021 NXP # # SPDX-License-Identifier: BSD-3-Clause """Module to covert Shadow register description EXCEL file to XML.""" - -from typing import Any +from typing import Any, Tuple import re import sys import os @@ -32,45 +31,54 @@ @click.option('-t', '--xls_type', type=int, default=1) @click.help_option('--help') @click.pass_context -def main(ctx: click.Context, xls: str, xml: str, xls_type: int=1) -> int: - +def main(ctx: click.Context, xls: str, xml: str, xls_type: int = 1) -> int: + """Main CLI function.""" if not isinstance(xls, str): return -1 if not isinstance(xml, str): xml = "" - try: - xls2xml_class = XLS_TYPES[str(xls_type)] - xls2xml_class(xls, xml, xls_type) - except Exception as exc: - print(str(exc)) - return -1 + ctx.obj = { + 'xls': xls, + 'xml': xml, + 'xls_type': xls_type + } return 0 @main.command() @click.pass_obj def convert(pass_obj: dict) -> None: """List supported Devices.""" - print("convert") + click.echo("convert") + try: + xls2xml_class = XLS_TYPES[str(pass_obj['xls_type'])] + xls2xml = xls2xml_class(pass_obj['xls'], pass_obj['xml'], pass_obj['xls_type']) + except Exception as exc: #pylint: disable=broad-except + click.echo(str(exc)) + return -1 + + xls2xml.convert() + xls2xml.registers.write_xml(xls2xml.xml_file_name) + click.echo(f"Written XML file ({xls2xml.xml_file_name})") + click.echo(str(xls2xml.registers)) class ShadowRegsXlsToXml(): "Class to convert XLSX to XML with shadow register description" - def __init__(self, xls_file: str, xml_file: str = "", xls_type: int=1) -> None: + def __init__(self, xls_file: str, xml_file: str = "", xls_type: int = 1) -> None: self.registers = Registers("Unknown") self.xls_type = xls_type self.header_cells = {} self.xml_file_name = xml_file if xml_file != "" else xls_file.replace(".xlsx", ".xml") - self.wb = None - print(os.path.dirname(os.path.realpath(__file__))) - self.wb = openpyxl.load_workbook(xls_file) - print(f"Loaded XLSX file ({xls_file})") - self.convert() - self.registers.write_xml(self.xml_file_name) - print(f"Written XML file ({self.xml_file_name})") - print(str(self.registers)) + self.workbook = None + self.worksheet = None + self.merged_cells = None + click.echo(os.path.dirname(os.path.realpath(__file__))) + self.workbook = openpyxl.load_workbook(xls_file) + click.echo(f"Loaded XLSX file ({xls_file})") def convert(self) -> None: + """Convert XLS to XML.""" raise NotImplementedError def _get_worksheet(self) -> Any: @@ -87,29 +95,29 @@ def _get_registers(self) -> None: def __del__(self) -> None: """Just close all open files.""" - if self.wb: - self.wb.close() - -class ShadowRegsXlsToXml_Type1(ShadowRegsXlsToXml): + if self.workbook: + self.workbook.close() +class ShadowRegsXlsToXmlType1(ShadowRegsXlsToXml): + """Support Type 1 XLS to convert to XML, RTxxx.""" def convert(self) -> None: - self.ws = self._get_worksheet() + """Convert XLS to XML.""" + self.worksheet = self._get_worksheet() #Get all merged cells - self.merged_cells = self.ws.merged_cells.ranges + self.merged_cells = self.worksheet.merged_cells.ranges self._get_header() self._get_registers() def _get_worksheet(self) -> Any: """Find the valid worksheet with the fuse map.""" - return self.wb.active + return self.workbook.active def _get_header(self) -> None: """Returns the dictionary with cells of header.""" - ret = {} for head in XLS_COLUMN_NAMES: self.header_cells[head] = self._find_cell_coor_by_val(head) - def _filterout_bitrange(self, name: str) -> (str, bool): + def _filterout_bitrange(self, name: str) -> Tuple[str, bool]: """Function filter out the bit ranges in various shapes from register name.""" bits_rev1 = re.search(r"_\d{1,4}_\d{1,4}$", name) bits_rev2 = re.search(r"\[\d{1,4}:\d{1,4}\]$", name) @@ -132,45 +140,59 @@ def _get_registers(self) -> None: offset_cr = cell_utils.coordinate_from_string(self.header_cells["Shadow Register Offset/bit offset"]) width_cr = cell_utils.coordinate_from_string(self.header_cells["Register Width / Field width"]) - s = 1 + regname_cr[1] - skip = 0 - for r in range(s, self.ws.max_row + 1): - cell = regname_cr[0] + str(r) - if skip > 0: - skip -= 1 - elif isinstance(self.ws[cell].value, str): + start = 1 + regname_cr[1] + regs_group_max = 0 + regs_group_cnt = 0 + for row in range(start, self.worksheet.max_row + 1): + cell = regname_cr[0] + str(row) + if isinstance(self.worksheet[cell].value, str): # We have a register, just Mask out the Fuse register only - access = self.ws[sr_access_cr[0] + str(r)].value \ - if isinstance(self.ws[sr_access_cr[0] + str(r)].value, str) else "" + access = self.worksheet[sr_access_cr[0] + str(row)].value \ + if isinstance(self.worksheet[sr_access_cr[0] + str(row)].value, str) else "" if any(x in access for x in ["rw", "ro", "wo"]): # Now we have just Shadow registers only # Some registers are defined multiply to store bigger data # those could be detected by merged description field - reg_name = self.ws[cell].value + reg_name = self.worksheet[cell].value # Now, normalize the name reg_name, reg_reverse = self._filterout_bitrange(reg_name) - cells = self._get_merged_by_first_cell(desc_cr[0] + str(r)) + + reg_offset = int(self.worksheet[offset_cr[0] + str(row)].value, 16) + reg_width = int(self.worksheet[width_cr[0] + str(row)].value) + reg_descr = self.worksheet[desc_cr[0] + str(row)].value or "N/A" + reg_name = reg_name.strip() + + cells = self._get_merged_by_first_cell(desc_cr[0] + str(row)) if cells is not None: # set the skip for next search cells = cells.split(':') - skip = cell_utils.coordinate_from_string(cells[1])[1] - \ + regs_group_max = cell_utils.coordinate_from_string(cells[1])[1] - \ cell_utils.coordinate_from_string(cells[0])[1] - - reg_offset = int(self.ws[offset_cr[0] + str(r)].value, 16) - reg_width = int(self.ws[width_cr[0] + str(r)].value) * (skip + 1) - reg_descr = self.ws[desc_cr[0] + str(r)].value - reg_name = reg_name.strip() - + regs_group_cnt = 0 + reg_group_descr = reg_descr + reg_group_reverse = reg_reverse + + if regs_group_max > 0: + reg_name = f"{reg_name}{regs_group_cnt}" + reg_descr = reg_group_descr + reg_reverse = reg_group_reverse + regs_group_cnt += 1 + if regs_group_cnt > regs_group_max: + regs_group_max = 0 + regs_group_cnt = 0 + + reg_descr = reg_descr.replace("\n", " ") register = RegsRegister(reg_name, reg_offset, reg_width, reg_descr, reg_reverse, access) self.registers.add_register(register) - cells = self._get_merged_by_first_cell(regname_cr[0] + str(r)) + cells = self._get_merged_by_first_cell(regname_cr[0] + str(row)) if cells is not None: # find the number of rows of the register description cells = cells.split(':') - reg_lines = cell_utils.coordinate_from_string(cells[1])[1] - cell_utils.coordinate_from_string(cells[0])[1] - self._get_bitfields(register, r, reg_lines + 1) + reg_lines = cell_utils.coordinate_from_string(cells[1])[1] \ + - cell_utils.coordinate_from_string(cells[0])[1] + self._get_bitfields(register, row, reg_lines + 1) def _get_bitfields(self, reg: Any, excel_row: int, excel_row_cnt: int) -> None: """Tried to find and fill up all register bitfields.""" @@ -187,30 +209,32 @@ def _get_bitfields(self, reg: Any, excel_row: int, excel_row_cnt: int) -> None: excel_row += 1 excel_row_cnt -= 1 - for r in range(excel_row, excel_row + excel_row_cnt): - cell = bitfieldname_cr[0] + str(r) - if isinstance(self.ws[cell].value, str): - bitfield_name = self.ws[cell].value - bitfield_offset = int(self.ws[offset_cr[0] + str(r)].value) - bitfield_width = int(self.ws[width_cr[0] + str(r)].value) - bitfield_descr = self.ws[desc_cr[0] + str(r)].value - bitfield_rv = self.ws[rv_cr[0] + str(r)].value - bitfield_rv = bitfield_rv if bitfield_rv is not None else "N/A" - bitf = RegsBitField(reg, - bitfield_name, - bitfield_offset, - bitfield_width, - bitfield_descr, - reset_val=bitfield_rv) - reg.add_bitfield(bitf) - - cells = self._get_merged_by_first_cell(bitfieldname_cr[0] + str(r)) + for row in range(excel_row, excel_row + excel_row_cnt): + cell = bitfieldname_cr[0] + str(row) + if isinstance(self.worksheet[cell].value, str): + bitfield_name = self.worksheet[cell].value + bitfield_offset = int(self.worksheet[offset_cr[0] + str(row)].value) + bitfield_width = int(self.worksheet[width_cr[0] + str(row)].value) + bitfield_descr = self.worksheet[desc_cr[0] + str(row)].value or "N/A" + bitfield_rv = self.worksheet[rv_cr[0] + str(row)].value or "N/A" + bitfield_descr = bitfield_descr.replace("\n", " ") + bitfield = RegsBitField( + reg, + bitfield_name, + bitfield_offset, + bitfield_width, + bitfield_descr, + reset_val=bitfield_rv + ) + reg.add_bitfield(bitfield) + + cells = self._get_merged_by_first_cell(bitfieldname_cr[0] + str(row)) if cells is not None: # find the number of rows of the register description cells = cells.split(':') reg_lines = cell_utils.coordinate_from_string(cells[1])[1] - \ cell_utils.coordinate_from_string(cells[0])[1] - self._get_enums(bitf, r, reg_lines + 1) + self._get_enums(bitfield, row, reg_lines + 1) def _get_enums(self, bitfield: Any, excel_row: int, excel_row_cnt: int) -> None: """Tried to find and fill up all register bitfields enumerations.""" @@ -218,25 +242,26 @@ def _get_enums(self, bitfield: Any, excel_row: int, excel_row_cnt: int) -> None: # There is no enums return - enumname_cr = cell_utils.coordinate_from_string(self.header_cells["Enum Name"]) + enum_name_cr = cell_utils.coordinate_from_string(self.header_cells["Enum Name"]) desc_cr = cell_utils.coordinate_from_string(self.header_cells["Description"]) value_cr = cell_utils.coordinate_from_string(self.header_cells["Value"]) excel_row += 1 excel_row_cnt -= 1 - for r in range(excel_row, excel_row + excel_row_cnt): - cell = enumname_cr[0] + str(r) - if isinstance(self.ws[cell].value, str): - enum_name = self.ws[cell].value - enum_descr = self.ws[desc_cr[0] + str(r)].value - enum_value: str = self.ws[value_cr[0] + str(r)].value + for row in range(excel_row, excel_row + excel_row_cnt): + cell = enum_name_cr[0] + str(row) + if isinstance(self.worksheet[cell].value, str): + enum_name = self.worksheet[cell].value + enum_descr = self.worksheet[desc_cr[0] + str(row)].value or "N/A" + enum_value: str = self.worksheet[value_cr[0] + str(row)].value + enum_descr = enum_descr.replace("\n", " ") if enum_value is None: - print(f"Warning: The Enum {enum_name} is missing and it will be skipped.") + click.echo(f"Warning: The Enum {enum_name} is missing and it will be skipped.") else: bitfield.add_enum(RegsEnum(enum_name, enum_value, enum_descr, bitfield.width)) - def _get_merged_by_first_cell(self, cell:str) -> str: + def _get_merged_by_first_cell(self, cell: str) -> str: """ Function returns the merged range by first cell.""" for merged in self.merged_cells: if merged.coord.find(cell+":") >= 0: @@ -244,45 +269,46 @@ def _get_merged_by_first_cell(self, cell:str) -> str: return None - def _find_cell_coor_by_val(self, value:Any, start:str = "", end:str = "") -> str: + def _find_cell_coor_by_val(self, value: Any, start: str = "", end: str = "") -> str: """Search engine for the cell values""" - if start == "" or start == None: + if start is None or start == "": start = "A1" - if end == "" or end == None: - end = utils.get_column_letter(self.ws.max_column) + str(self.ws.max_row) - - s = cell_utils.coordinate_from_string(start) - e = cell_utils.coordinate_from_string(end) - sc = utils.column_index_from_string(s[0]) - sr = s[1] - ec = utils.column_index_from_string(e[0]) - er = e[1] - - for r in range(sr, er+1): - for c in range(sc, ec+1): - val = self.ws[utils.get_column_letter(c) + str(r)].value + if end is None or end == "": + end = utils.get_column_letter(self.worksheet.max_column) + str(self.worksheet.max_row) + + start_cell = cell_utils.coordinate_from_string(start) + end_cell = cell_utils.coordinate_from_string(end) + start_column = utils.column_index_from_string(start_cell[0]) + start_row = start_cell[1] + end_column = utils.column_index_from_string(end_cell[0]) + end_row = end_cell[1] + + for row in range(start_row, end_row+1): + for column in range(start_column, end_column+1): + val = self.worksheet[utils.get_column_letter(column) + str(row)].value if isinstance(val, str): val = val.replace("\n", " ") val = val.replace(" ", " ") if value == val: - return utils.get_column_letter(c) + str(r) + return utils.get_column_letter(column) + str(row) return None -class ShadowRegsXlsToXml_Type2(ShadowRegsXlsToXml): - +class ShadowRegsXlsToXmlType2(ShadowRegsXlsToXml): + """Convert Type2 of XLS (RTxxxx).""" def convert(self) -> None: - self.ws = self._get_worksheet() + self.worksheet = self._get_worksheet() #Get all merged cells self._get_header() self._get_registers() def _get_worksheet(self) -> None: """Find the valid worksheet with the fuse map.""" - return self.wb["Fuse Definitions"] + return self.workbook["Fuse Definitions"] def _get_header(self) -> None: + """Returns header of sheet.""" self.header_cells["reg_base"] = "A" self.header_cells["fuse_address"] = "B" self.header_cells["fuse_index"] = "D" @@ -294,55 +320,49 @@ def _get_header(self) -> None: self.header_cells["customer_visible"] = "L" def _get_regbase(self, line: int) -> int: - try: - base = int(self.ws[self.header_cells["reg_base"] + str(line)].value, 16) - except Exception as exc: - base = -1 - return base + """Return register base address.""" + return int(self.worksheet[self.header_cells["reg_base"] + str(line)].value, 16) def _get_fusename(self, line: int) -> int: + """Return Fuse name.""" try: - name = self.ws[self.header_cells["fuse_name"] + str(line)].value - except Exception as exc: + name = self.worksheet[self.header_cells["fuse_name"] + str(line)].value + except Exception: #pylint: disable=broad-except name = "Unknown name :-(" return name def _get_fuseoffset(self, line: int) -> int: - - try: - reg_offset_bits = (self._get_regbase(line) - 0x400) - fuse_offset = self.ws[self.header_cells["fuse_index"] + str(line)].value - fuse_offset = fuse_offset - reg_offset_bits - except Exception as exc: - fuse_offset = -1 + """Gets Fuse offset.""" + reg_offset_bits = (self._get_regbase(line) - 0x400) + fuse_offset = self.worksheet[self.header_cells["fuse_index"] + str(line)].value + fuse_offset = fuse_offset - reg_offset_bits return fuse_offset def _get_fusewidth(self, line: int) -> int: - - try: - fuse_width = self.ws[self.header_cells["fuse_width"] + str(line)].value - except Exception as exc: - fuse_width = -1 - return fuse_width + """Get fuse width.""" + return self.worksheet[self.header_cells["fuse_width"] + str(line)].value def _get_fusedescription(self, line: int) -> int: + """Return fuse description.""" try: - fuse_description = self.ws[self.header_cells["fuse_descr"] + str(line)].value - except Exception as exc: + fuse_description = self.worksheet[self.header_cells["fuse_descr"] + str(line)].value + except Exception: #pylint: disable=broad-except fuse_description = "There is no any special description" return fuse_description def _get_fuse_resetvalue(self, line: int) -> int: + """Return fuse reset value.""" try: - fuse_resetvalue = self.ws[self.header_cells["burned_value"] + str(line)].value - except Exception as exc: - fuse_resetvalue = "N/A" + fuse_resetvalue = self.worksheet[self.header_cells["burned_value"] + str(line)].value + except Exception: #pylint: disable=broad-except + fuse_resetvalue = "0" return fuse_resetvalue - def _get_fuse_bitfield_info(self, line: int) -> (int,int): + def _get_fuse_bitfield_info(self, line: int) -> Tuple[int, int]: + """Return Fuse bitfield information.""" try: fuse_width = self._get_fusewidth(line) - fuse_address = self.ws[self.header_cells["fuse_address"] + str(line)].value + fuse_address = self.worksheet[self.header_cells["fuse_address"] + str(line)].value pattern = re.compile(r'\[([^)]*)\]') offsets = pattern.findall(fuse_address)[0] if offsets.count(":") > 0: @@ -351,51 +371,51 @@ def _get_fuse_bitfield_info(self, line: int) -> (int,int): offset = int(offsets[0]) else: offset = int(offsets) - except Exception as exc: - print(f"Issue with get the getting bitfield info ({str(exc)})") + except Exception as exc: #pylint: disable=broad-except + click.echo(f"Issue with get the getting bitfield info ({str(exc)})") return offset, fuse_width def _get_registers(self) -> None: + """Return all registers from XLS file.""" # Start line in excel style 2 is 3! reg_base = 0 try: - for r in range(3, self.ws.max_row + 1): - new_reg_base = self._get_regbase(r) - if new_reg_base == -1: - break + for row in range(3, self.worksheet.max_row + 1): + new_reg_base = self._get_regbase(row) if new_reg_base != reg_base: # This is new register, just create it reg_base = new_reg_base reg_name = f"REG_0x{reg_base:04X}" reg_offset = 0x400 - reg_base reg_width = 32 # TODO solve that fields - reg_dscr = f"This is description string of {reg_name} register" + reg_description = f"This is description string of {reg_name} register" reg_reverse = False reg_access = "RW" - reg = RegsRegister(reg_name, reg_offset, reg_width, reg_dscr, reg_reverse, reg_access) + reg = RegsRegister(reg_name, reg_offset, reg_width, reg_description, reg_reverse, reg_access) self.registers.add_register(reg) # we have added register, so this is about a adding of bitfield - bitfield_name = self._get_fusename(r) - bitfield_offset, bitfield_width = self._get_fuse_bitfield_info(r) - bitfield_descr = self._get_fusedescription(r) - bitfield_rv = self._get_fuse_resetvalue(r) - bitf = RegsBitField(reg, - bitfield_name, - bitfield_offset, - bitfield_width, - bitfield_descr, - reset_val=bitfield_rv) - reg.add_bitfield(bitf) - except Exception as exc: - print(f"Unwanted exception during getting registers({str(exc)})") - - - - -XLS_TYPES = {"1": ShadowRegsXlsToXml_Type1, - "2": ShadowRegsXlsToXml_Type2} + bitfield_name = self._get_fusename(row) + bitfield_offset, bitfield_width = self._get_fuse_bitfield_info(row) + bitfield_descr = self._get_fusedescription(row) + bitfield_rv = self._get_fuse_resetvalue(row) + bitfield = RegsBitField( + reg, + bitfield_name, + bitfield_offset, + bitfield_width, + bitfield_descr, + reset_val=bitfield_rv + ) + reg.add_bitfield(bitfield) + except Exception as exc: #pylint: disable=broad-except + click.echo(f"Unwanted exception during getting registers({str(exc)})") + +XLS_TYPES = { + "1": ShadowRegsXlsToXmlType1, + "2": ShadowRegsXlsToXmlType2 +} if __name__ == "__main__": sys.exit(main()) # pragma: no cover # pylint: disable=no-value-for-parameter diff --git a/tools/test_debuggers.py b/tools/test_debuggers.py deleted file mode 100644 index 0222b838..00000000 --- a/tools/test_debuggers.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 2021 NXP -# -# SPDX-License-Identifier: BSD-3-Clause -"""Module for testing debuggers support in SPSDK.""" - -import logging -import sys -import random -import click - -from spsdk.exceptions import SPSDKError -from spsdk.apps.utils import INT - -from spsdk.debuggers.utils import DebugProbeUtils - -logger = logging.getLogger("DebugProbesUtils") -LOG_LEVEL_NAMES = [name.lower() for name in logging._nameToLevel] - -@click.group() -@click.option('-i', '--interface') -@click.option('-d', '--debug', 'log_level', metavar='LEVEL', default='debug', - help=f'Set the level of system logging output. ' - f'Available options are: {", ".join(LOG_LEVEL_NAMES)}', - type=click.Choice(LOG_LEVEL_NAMES)) -@click.option('-s', '--serial-no') -@click.option('-ip', '--ip', 'ip_addr') -@click.pass_context -def main(ctx: click.Context, interface: str, log_level: str, - serial_no: str, ip_addr: str) -> int: - """NXP Debug Mailbox Tool.""" - logging.basicConfig(level=log_level.upper()) - logger.setLevel(level=log_level.upper()) - - # Get the Debug probe object - try: - #TODO solve following parameters: - # ip_addr - # tool - debug_probes = DebugProbeUtils.get_connected_probes(interface=interface, hardware_id=serial_no) - selected_probe = debug_probes.select_probe() - debug_probe = DebugProbeUtils.get_probe(interface=selected_probe.interface, - hardware_id=selected_probe.hardware_id) - debug_probe.open() - - ctx.obj = { - 'debug_probe': debug_probe - } - - except: - logger.error("Test of SPSDK debug probes failed") - return -1 - return 0 - -@main.command() -@click.option('-a', '--address', type=INT(), help='Testing address', default="0x20000000") -@click.option('-s', '--size', type=INT(), help='Testing block size', default="1") -@click.pass_obj -def regs(pass_obj: dict, address: int, size: int) -> None: - """Test Shadow registers.""" - if size == 0: - logger.error("Invalid test vector size") - return - error_cnt = 0 - - try: - probe = pass_obj['debug_probe'] - test_vector = [random.randint(0, 0xffffffff) for x in range(size)] - - for i in range(size): - probe.mem_reg_write(address + i * 4, test_vector[i]) - - for i in range(size): - if test_vector[i] != probe.mem_reg_read(address + i * 4): - error_cnt += 1 - - if error_cnt == 0: - logger.info("Debug Probe shadow register test ends successfully") - else: - logger.error(f"Debug Probe shadow register test ends with {error_cnt} fails from {size}") - except Exception as exc: - logger.error(f"Debug Probe shadow register test failed! ({str(exc)})") - -if __name__ == "__main__": - sys.exit(main()) # pragma: no cover # pylint: disable=no-value-for-parameter \ No newline at end of file diff --git a/tox.ini b/tox.ini index 05289083..dacbe8e1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ # content of: tox.ini , put in same dir as setup.py [tox] envlist = py36,py37,py38 -requires = pip < 20 [testenv] # install testing framework
Register