From 5da31d96a020bd65e5834ea5ac1b68327ea965ef Mon Sep 17 00:00:00 2001 From: Maria Wisniewska Date: Thu, 6 Apr 2023 15:11:43 +0200 Subject: [PATCH] Release 1.10.0 --- .bumpversion.cfg | 2 +- .github/workflows/python-ci.yml | 68 +- .pre-commit-config.yaml | 4 +- .pylintrc | 2 +- .readthedocs.yml | 2 +- SW_Content_Register_SPSDK.txt | 2 +- codecheck.py | 188 +- docs/apps/blhost.rst | 4 + docs/apps/images.rst | 8 +- docs/exts/generate_schemas.py | 34 +- docs/release_notes.rst | 35 +- docs/spsdk.rst | 2 +- docs/usage/installation.rst | 2 +- examples/dat/.gitignore | 1 - examples/dat/README.md | 37 - examples/dat/dck.pem | 28 - examples/dat/dck.pub | 8 - examples/dat/dck_rsa_2048.yml | 116 - examples/dat/hsm/sahsm.py | 44 - examples/dat/hsm/sasp.py | 51 - examples/dat/p0_cert0_2048.pub | 8 - examples/dat/p1_cert0_2048.pub | 8 - examples/data/cfpa_lpc55s6x_test.yaml | 37 + examples/data/cfpa_test.json | 10 - .../lpc55sxx_secure_boot.ipynb | 709 ++- .../lpc55sxx_secure_fw_update.ipynb | 758 ++- examples/lpc55xx_tz_pfr.py | 11 +- examples/sdp_mboot.py | 2 +- pyproject.toml | 2 +- release_notes.txt | 52 +- requirements-develop.txt | 27 +- requirements.txt | 13 +- setup.py | 5 +- spsdk/__version__.py | 2 +- spsdk/apps/blhost.py | 67 +- spsdk/apps/elftosb.py | 29 +- spsdk/apps/elftosb_utils/sb_31_helper.py | 17 +- spsdk/apps/ifr.py | 200 +- spsdk/apps/nxpcertgen.py | 10 +- spsdk/apps/nxpcrypto.py | 5 +- spsdk/apps/nxpdebugmbox.py | 116 +- spsdk/apps/nxpdevhsm.py | 218 +- spsdk/apps/nxpimage.py | 469 +- spsdk/apps/nxpkeygen.py | 3 +- spsdk/apps/pfr.py | 140 +- spsdk/apps/shadowregs.py | 81 +- spsdk/apps/tp_utils.py | 16 +- spsdk/apps/tpconfig.py | 12 +- spsdk/apps/tphost.py | 15 +- spsdk/crypto/__init__.py | 1 - spsdk/crypto/keys_management.py | 2 +- spsdk/crypto/loaders.py | 15 +- spsdk/crypto/signature_provider.py | 140 +- spsdk/dat/dac_packet.py | 2 +- spsdk/dat/dar_packet.py | 9 +- spsdk/dat/debug_credential.py | 17 +- spsdk/dat/debug_mailbox.py | 6 +- spsdk/dat/dm_commands.py | 4 +- .../data/ahab/{database.yml => database.yaml} | 6 +- .../data/ahab/{sch_ahab.yml => sch_ahab.yaml} | 15 +- ...sch_signed_msg.yml => sch_signed_msg.yaml} | 14 +- spsdk/data/ifr/database.yaml | 8 + spsdk/data/ifr/k32w1xx_a0.xml | 5 + spsdk/data/ifr/k32w1xx_a1.xml | 99 +- spsdk/data/ifr/kw45xx_a0.xml | 5 + spsdk/data/ifr/kw45xx_a1.xml | 103 +- .../{database.yml => database.yaml} | 37 +- .../{sch_bimg.yml => sch_bimg.yaml} | 61 +- .../image/{database.yml => database.yaml} | 2 +- spsdk/data/image/database_sb31.yaml | 15 + .../image/fcb/{database.yml => database.yaml} | 18 +- spsdk/data/image/fcb/lpc55s3x_flexspi_nor.xml | 7 +- spsdk/data/image/fcb/rt101x_flexspi_nor.xml | 219 + spsdk/data/image/fcb/rt102x_flexspi_nor.xml | 219 + spsdk/data/image/fcb/rt104x_flexspi_nor.xml | 219 + spsdk/data/image/fcb/rt105x_flexspi_nor.xml | 7 +- spsdk/data/image/fcb/rt106x_flexspi_nor.xml | 7 +- spsdk/data/image/fcb/rt116x_flexspi_nor.xml | 219 + spsdk/data/image/fcb/rt117x_flexspi_nor.xml | 7 +- spsdk/data/image/fcb/rt118x_flexspi_nor.xml | 5 + spsdk/data/image/fcb/rt5xx_flexspi_nor.xml | 7 +- spsdk/data/image/fcb/rt6xx_flexspi_nor.xml | 7 +- .../image/fcb/{sch_fcb.yml => sch_fcb.yaml} | 0 .../image/{sch_binary.yml => sch_binary.yaml} | 12 +- .../image/{sch_mbimg.yml => sch_mbimg.yaml} | 119 +- .../data/image/{sch_sb3.yml => sch_sb3.yaml} | 10 +- spsdk/data/image/{sch_tz.yml => sch_tz.yaml} | 14 +- .../xmcd/{database.yml => database.yaml} | 0 spsdk/data/image/xmcd/flexspi_ram_full.xml | 7 +- .../image/xmcd/flexspi_ram_simplified.xml | 24 +- spsdk/data/image/xmcd/header.xml | 7 +- .../xmcd/{sch_xmcd.yml => sch_xmcd.yaml} | 0 spsdk/data/image/xmcd/semc_sdram_full.xml | 7 +- .../data/image/xmcd/semc_sdram_simplified.xml | 7 +- ...certgen_config.yml => certgen_config.yaml} | 4 + ...mplate_config.yml => template_config.yaml} | 8 +- spsdk/data/nxpdevhsm/database.yaml | 3 + spsdk/data/pfr/cfpa/database.yaml | 10 +- spsdk/data/pfr/cfpa/lpc550x_a.xml | 117 +- spsdk/data/pfr/cfpa/lpc551x_a.xml | 117 +- spsdk/data/pfr/cfpa/lpc552x_1b.xml | 117 +- spsdk/data/pfr/cfpa/lpc553x_0a.xml | 5 + spsdk/data/pfr/cfpa/lpc553x_1a.xml | 81 +- spsdk/data/pfr/cfpa/lpc55s0x_a.xml | 213 +- spsdk/data/pfr/cfpa/lpc55s1x_a.xml | 213 +- spsdk/data/pfr/cfpa/lpc55s2x_1b.xml | 247 +- spsdk/data/pfr/cfpa/lpc55s3x_0a.xml | 5 + spsdk/data/pfr/cfpa/lpc55s3x_1a.xml | 143 +- spsdk/data/pfr/cfpa/lpc55s6x_1b.xml | 245 +- spsdk/data/pfr/cfpa/nhs52s04.xml | 5 + spsdk/data/pfr/cmpa/database.yaml | 4 + spsdk/data/pfr/cmpa/lpc550x_a.xml | 185 +- spsdk/data/pfr/cmpa/lpc551x_a.xml | 193 +- spsdk/data/pfr/cmpa/lpc552x_1b.xml | 226 +- spsdk/data/pfr/cmpa/lpc553x_0a.xml | 5 + spsdk/data/pfr/cmpa/lpc553x_1a.xml | 413 +- spsdk/data/pfr/cmpa/lpc55s0x_a.xml | 347 +- spsdk/data/pfr/cmpa/lpc55s1x_a.xml | 357 +- spsdk/data/pfr/cmpa/lpc55s2x_1b.xml | 353 +- spsdk/data/pfr/cmpa/lpc55s3x_0a.xml | 5 + spsdk/data/pfr/cmpa/lpc55s3x_1a.xml | 673 ++- spsdk/data/pfr/cmpa/lpc55s6x_1b.xml | 351 +- spsdk/data/pfr/cmpa/nhs52s04.xml | 5 + spsdk/data/pfr/pfrc/database.yaml | 2 +- spsdk/data/pfr/pfrc/rules_common.yaml | 17 +- spsdk/data/pfr/pfrc/rules_lpc55s3x.yaml | 4 +- spsdk/data/pfr/pfrc/rules_lpc55xx.yaml | 7 +- spsdk/data/shadowregs/database.yaml | 4 + spsdk/data/shadowregs/imxrt595_b0.xml | 7 +- spsdk/data/shadowregs/imxrt685_b0.xml | 5 + spsdk/data/tp/database.yaml | 4 + spsdk/data/tp/sch_tp.yaml | 2 +- ...emplate.yml => tpconfig_cfg_template.yaml} | 4 + ..._template.yml => tphost_cfg_template.yaml} | 4 + spsdk/data/tz_presets/database.yaml | 4 + spsdk/data/tz_presets/kw45xx_a0.yaml | 4 + spsdk/data/tz_presets/lpc55s0x.yaml | 4 + spsdk/data/tz_presets/lpc55s1x.yaml | 4 + spsdk/data/tz_presets/lpc55s3x_a0.yaml | 4 + spsdk/data/tz_presets/lpc55s3x_a1.yaml | 4 + spsdk/data/tz_presets/lpc55xx.yaml | 4 + spsdk/data/tz_presets/lpc55xx_a0.yaml | 4 + spsdk/data/tz_presets/lpc55xx_a1.yaml | 4 + spsdk/data/tz_presets/nhs52s04.yaml | 4 + spsdk/data/tz_presets/rt5xx.yaml | 4 + spsdk/data/tz_presets/rt5xx_a0.yaml | 4 + spsdk/data/tz_presets/rt6xx.yaml | 4 + spsdk/data/tz_presets/rt6xx_a0.yaml | 4 + spsdk/data/tz_presets/rt6xx_b0.yaml | 4 + spsdk/data/utils/bee/database.yaml | 2 +- .../utils/bee/{sch_bee.yml => sch_bee.yaml} | 0 .../utils/iee/{database.yml => database.yaml} | 17 +- spsdk/data/utils/iee/fuses_rt117x.xml | 5 + .../utils/iee/{sch_iee.yml => sch_iee.yaml} | 99 +- .../otfad/{database.yml => database.yaml} | 57 +- spsdk/data/utils/otfad/fuses_rt117x.xml | 94 + spsdk/data/utils/otfad/fuses_rt118x.xml | 5 + spsdk/data/utils/otfad/fuses_rt5xx.xml | 5 + spsdk/data/utils/otfad/fuses_rt6xx.xml | 5 + .../otfad/{sch_otfad.yml => sch_otfad.yaml} | 22 +- .../utils/{sch_crypto.yml => sch_crypto.yaml} | 115 +- spsdk/debuggers/debug_probe.py | 6 +- spsdk/debuggers/debug_probe_pemicro.py | 19 +- spsdk/debuggers/debug_probe_pyocd.py | 9 +- spsdk/debuggers/utils.py | 2 +- spsdk/exceptions.py | 12 + spsdk/image/__init__.py | 7 +- spsdk/image/ahab/__init__.py | 6 +- spsdk/image/ahab/ahab_container.py | 20 +- spsdk/image/ahab/signed_msg.py | 2 +- spsdk/image/bee.py | 18 +- spsdk/image/bootable_image/__init__.py | 6 +- spsdk/image/bootable_image/bimg.py | 1464 ++--- spsdk/image/commands.py | 9 +- spsdk/image/fcb/__init__.py | 6 +- spsdk/image/hab/__init__.py | 8 + spsdk/image/hab/hab_container.py | 247 + spsdk/image/header.py | 24 +- spsdk/image/images.py | 35 +- spsdk/image/mbi_mixin.py | 88 +- spsdk/image/mbimg.py | 53 +- spsdk/image/segments.py | 40 +- spsdk/image/segments_base.py | 4 +- spsdk/image/trustzone.py | 3 +- spsdk/image/xmcd/__init__.py | 4 +- spsdk/image/xmcd/xmcd.py | 137 +- spsdk/mboot/commands.py | 1 + spsdk/mboot/error_codes.py | 4 + spsdk/mboot/interfaces/buspal_i2c.py | 4 +- spsdk/mboot/interfaces/buspal_spi.py | 4 +- spsdk/mboot/interfaces/uart.py | 10 +- spsdk/mboot/interfaces/usb.py | 5 +- spsdk/mboot/interfaces/usbsio.py | 15 +- spsdk/mboot/mcuboot.py | 31 +- spsdk/pfr/pfrc.py | 1 - spsdk/pfr/translator.py | 2 +- spsdk/sbfile/sb1/images.py | 6 +- spsdk/sbfile/sb2/commands.py | 9 +- spsdk/sbfile/sb2/headers.py | 2 +- spsdk/sbfile/sb2/images.py | 37 +- spsdk/sbfile/sb2/sections.py | 16 +- spsdk/sbfile/sb2/sly_bd_parser.py | 11 +- spsdk/sbfile/sb31/commands.py | 1 + spsdk/sbfile/sb31/images.py | 117 +- spsdk/sdp/interfaces/uart.py | 8 +- spsdk/sdp/interfaces/usb.py | 2 + spsdk/shadowregs/shadowregs.py | 77 +- spsdk/tp/adapters/tpdev_scard.py | 3 +- spsdk/tp/tphost.py | 13 +- spsdk/tp/utils.py | 15 +- spsdk/utils/crypto/__init__.py | 10 +- spsdk/utils/crypto/backend_internal.py | 14 +- spsdk/utils/crypto/cert_blocks.py | 477 +- spsdk/utils/crypto/certificate.py | 16 +- spsdk/utils/crypto/iee.py | 93 +- spsdk/utils/crypto/otfad.py | 53 +- spsdk/utils/database.py | 34 +- spsdk/utils/easy_enum.py | 18 +- spsdk/utils/exceptions.py | 4 +- spsdk/utils/images.py | 39 +- spsdk/utils/misc.py | 44 +- spsdk/utils/registers.py | 2 +- spsdk/utils/schema_validator.py | 334 +- spsdk/utils/serial_buspal_proxy.py | 5 +- tests/apps/data/certgen_config.yaml | 4 + tests/crypto/test_sign_provider.py | 6 +- tests/dat/data/dck_rsa2048_rot_meta_cert.yml | 4 + tests/dat/data/new_dck_rsa2048.yml | 4 + tests/dat/data/new_dck_rsa2048_invalid.yml | 4 + tests/dat/data/new_dck_secp256.yml | 4 + tests/dat/data/new_dck_secp256_lpc55s3x.yml | 4 + .../new_dck_secp256_lpc55s3x_not_empty.yml | 4 + tests/dat/data/new_dck_secp384_lpc55s3x.yml | 4 + .../new_dck_secp384_lpc55s3x_not_empty.yml | 4 + tests/dat/data/no_key_dck_rsa_2048.yml | 4 + tests/dat/data/org_dck_rsa_2048.yml | 4 + tests/dat/data/plugin_dck_rsa_2048.yml | 4 + tests/dat/data/signature_provider.py | 8 +- tests/dat/test_dar_packet.py | 2 - .../workspace/cfgs/lpc55s3x/cert_256_256.json | 15 + .../workspace/cfgs/lpc55s3x/cert_384_256.json | 15 + .../workspace/cfgs/lpc55s3x/cert_384_384.json | 15 + .../cfgs/lpc55s3x/mb_xip_384_256_cert.json | 12 + .../lpc55s3x/mb_xip_384_256_cert_invalid.json | 31 + .../cfgs/lpc55s3x/mb_xip_384_384_cert.json | 12 + .../cfgs/lpc55s3x/sb3_256_256_cert.json | 35 + .../cfgs/lpc55s3x/sb3_384_256_cert.json | 35 + .../cfgs/lpc55s3x/sb3_384_384_cert.json | 29 + .../sb3_test_384_384_unencrypted.json | 12 +- .../output_images/lpc55s3x/cert_256_256.bin | Bin 0 -> 716 bytes .../output_images/lpc55s3x/cert_384_256.bin | Bin 0 -> 476 bytes .../output_images/lpc55s3x/cert_384_384.bin | Bin 0 -> 876 bytes .../lpc55s3x/sb3_test_384_384_unencrypted.sb3 | Bin 23564 -> 23564 bytes tests/elftosb/test_bd_compiler.py | 108 +- tests/elftosb/test_elftosb_cfg_template.py | 2 +- tests/elftosb/test_elftosb_sb31.py | 2 - tests/elftosb/test_lexer.py | 3 +- tests/ifr/data/template45.yaml | 4 + tests/image/ahab/test_ahab.py | 2 +- tests/image/images/test_bootimage_rt10xx.py | 3 +- .../image/mbi/data/lpc55s6x_int_xip_plain.yml | 6 +- tests/image/mbi/data/test_app_table.yaml | 4 + tests/image/mbi/test_mbi.py | 64 +- tests/image/segments/test_xmcd.py | 14 + tests/mboot/blhost/test_blhost_cli.py | 4 +- tests/mboot/devices/virtual_device.yaml | 4 + tests/mboot/test_commands.py | 4 +- tests/mboot/test_mboot_api.py | 4 +- tests/mboot/virtual_device.py | 6 +- tests/mcu_examples/test_rt10xx.py | 29 +- tests/mcu_examples/test_rt5xx.py | 75 +- .../nxpcertgen/data/cert_config_secp256.yaml | 4 + tests/nxpcertgen/data/certgen_config.yaml | 4 + tests/nxpdevhsm/data/cfg_sb3_keyblob.yaml | 4 + tests/nxpdevhsm/data/cfg_sb3_keyblob4.yaml | 4 + tests/nxpdevhsm/data/cfg_sb3_load.yaml | 4 + tests/nxpdevhsm/test_nxpdevhsm.py | 5 +- tests/nxpimage/data/ahab/config_ctcm.json | 2 +- tests/nxpimage/data/ahab/config_fspi1.json | 44 +- tests/nxpimage/data/ahab/config_fspi2.json | 44 +- .../nxpimage/data/ahab/config_fspi2eleFw.json | 50 +- .../data/ahab/ctcm_cm33_encrypted_img.yaml | 7 +- .../data/ahab/ctcm_cm33_signed_cert.yaml | 9 +- .../data/ahab/ctcm_cm33_signed_cert_nx.yaml | 9 +- .../data/ahab/ctcm_cm33_signed_cert_sb.yaml | 9 +- .../data/ahab/ctcm_cm33_signed_img.json | 2 +- tests/nxpimage/data/ahab/return_lc.yaml | 8 +- .../data/bee/both_engines_ctr/bee_config.yaml | 6 +- .../bee_config.yaml | 6 +- .../bee_config.yaml | 6 +- .../bee_config.yaml | 6 +- .../{ => flexspi_nor}/application.bin | Bin .../lpc55s3x/{ => flexspi_nor}/config.yaml | 10 +- .../lpc55s3x/{ => flexspi_nor}/fcb.bin | Bin .../{ => flexspi_nor}/merged_image.bin | Bin .../rt101x/flexspi_nor/config.yaml | 13 + .../{rt117x => rt101x/flexspi_nor}/fcb.bin | Bin .../flexspi_nor/hab_container.bin} | Bin 18000 -> 22096 bytes .../{rt5xx => rt101x/flexspi_nor}/keyblob.bin | Bin .../flexspi_nor}/merged_image.bin | Bin 26192 -> 26192 bytes .../rt102x/flexspi_nor/bee_header_0.bin | Bin 0 -> 512 bytes .../rt102x/flexspi_nor/bee_header_1.bin | Bin 0 -> 512 bytes .../rt102x/flexspi_nor/config.yaml | 14 + .../bootable_image/rt102x/flexspi_nor/fcb.bin | Bin 0 -> 512 bytes .../rt102x/flexspi_nor/hab_container.bin | Bin 0 -> 9606 bytes .../rt102x/flexspi_nor/merged_image.bin | Bin 0 -> 13702 bytes .../bootable_image/rt105x/application.bin | Bin 12808 -> 0 bytes .../data/bootable_image/rt105x/bdi.bin | Bin 12 -> 0 bytes .../data/bootable_image/rt105x/config.yaml | 19 - .../rt105x/flexspi_nor/bee_header_0.bin | Bin 0 -> 512 bytes .../rt105x/flexspi_nor/bee_header_1.bin | Bin 0 -> 512 bytes .../rt105x/flexspi_nor/config.yaml | 14 + .../bootable_image/rt105x/flexspi_nor/fcb.bin | Bin 0 -> 512 bytes .../rt105x/flexspi_nor/hab_container.bin | Bin 0 -> 10872 bytes .../rt105x/flexspi_nor/merged_image.bin | Bin 0 -> 14968 bytes .../data/bootable_image/rt105x/ivt.bin | Bin 32 -> 0 bytes .../bootable_image/rt105x/merged_image.bin | Bin 21000 -> 0 bytes .../bootable_image/rt106x/application.bin | Bin 12808 -> 0 bytes .../data/bootable_image/rt106x/bdi.bin | Bin 12 -> 0 bytes .../data/bootable_image/rt106x/config.yaml | 19 - .../rt106x/flexspi_nor/bee_header_0.bin | Bin 0 -> 512 bytes .../rt106x/flexspi_nor/bee_header_1.bin | Bin 0 -> 512 bytes .../rt106x/flexspi_nor/config.yaml | 14 + .../bootable_image/rt106x/flexspi_nor/fcb.bin | Bin 0 -> 512 bytes .../rt106x/flexspi_nor/hab_container.bin | Bin 0 -> 11060 bytes .../rt106x/flexspi_nor/merged_image.bin | Bin 0 -> 15156 bytes .../data/bootable_image/rt106x/ivt.bin | Bin 32 -> 0 bytes .../bootable_image/rt106x/merged_image.bin | Bin 21000 -> 0 bytes .../rt116x/flexspi_nand/config.yaml | 11 + .../rt116x/flexspi_nand/hab_container.bin | Bin 0 -> 22492 bytes .../rt116x/flexspi_nand/merged_image.bin | Bin 0 -> 23516 bytes .../rt116x/flexspi_nor/config.yaml | 14 + .../bootable_image/rt116x/flexspi_nor/fcb.bin | Bin 0 -> 512 bytes .../rt116x/flexspi_nor/hab_container.bin | Bin 0 -> 22096 bytes .../{rt6xx => rt116x/flexspi_nor}/keyblob.bin | Bin .../flexspi_nor}/keystore.bin | Bin .../rt116x/flexspi_nor/merged_image.bin | Bin 0 -> 26192 bytes .../rt116x/semc_nand/config.yaml | 11 + .../rt116x/semc_nand/hab_container.bin | Bin 0 -> 22488 bytes .../rt116x/semc_nand/merged_image.bin | Bin 0 -> 23512 bytes .../data/bootable_image/rt117x/bdi.bin | Bin 12 -> 0 bytes .../data/bootable_image/rt117x/config.yaml | 19 - .../data/bootable_image/rt117x/fcb.yaml | 349 -- .../rt117x/flexspi_nand/config.yaml | 11 + .../rt117x/flexspi_nand/hab_container.bin | Bin 0 -> 25708 bytes .../rt117x/flexspi_nand/merged_image.bin | Bin 0 -> 26732 bytes .../rt117x/flexspi_nor/config.yaml | 14 + .../bootable_image/rt117x/flexspi_nor/fcb.bin | Bin 0 -> 512 bytes .../rt117x/flexspi_nor/hab_container.bin | Bin 0 -> 22096 bytes .../rt117x/flexspi_nor/keyblob.bin | Bin 0 -> 256 bytes .../flexspi_nor}/keystore.bin | Bin .../rt117x/flexspi_nor/merged_image.bin | Bin 0 -> 26192 bytes .../data/bootable_image/rt117x/ivt.bin | Bin 32 -> 0 bytes .../rt117x/semc_nand/config.yaml | 11 + .../rt117x/semc_nand/hab_container.bin | Bin 0 -> 28744 bytes .../rt117x/semc_nand/merged_image.bin | Bin 0 -> 29768 bytes .../data/bootable_image/rt117x/xmcd.bin | Bin 13 -> 0 bytes .../{ => flexspi_nor}/ahab_container.bin | Bin .../rt118x/{ => flexspi_nor}/config.yaml | 4 + .../rt118x/{ => flexspi_nor}/fcb.bin | Bin .../rt118x/{ => flexspi_nor}/merged_image.bin | Bin .../rt5xx/{ => flexspi_nor}/application.bin | Bin .../{rt6xx => rt5xx/flexspi_nor}/config.yaml | 14 +- .../rt5xx/{ => flexspi_nor}/fcb.bin | Bin .../rt5xx/flexspi_nor/keyblob.bin | Bin 0 -> 256 bytes .../rt5xx/flexspi_nor/keystore.bin | Bin 0 -> 2048 bytes .../rt5xx/{ => flexspi_nor}/merged_image.bin | Bin .../rt6xx/{ => flexspi_nor}/application.bin | Bin .../{rt5xx => rt6xx/flexspi_nor}/config.yaml | 14 +- .../rt6xx/{ => flexspi_nor}/fcb.bin | Bin .../rt6xx/flexspi_nor/keyblob.bin | Bin 0 -> 256 bytes .../rt6xx/flexspi_nor/keystore.bin | Bin 0 -> 2048 bytes .../rt6xx/{ => flexspi_nor}/merged_image.bin | Bin .../lpc55s3x/fcb_lpc55s3x_flexspi_nor.yaml | 4 + .../fcb/rt105x/fcb_rt105x_flexspi_nor.yaml | 4 + .../fcb/rt106x/fcb_rt106x_flexspi_nor.yaml | 4 + .../fcb/rt117x/fcb_rt117x_flexspi_nor.yaml | 4 + .../data/fcb/rt5xx/fcb_rt5xx_flexspi_nor.yaml | 4 + .../data/fcb/rt6xx/fcb_rt6xx_flexspi_nor.yaml | 4 + ...rt1160_iled_blinky_cm7_xip_mdk_unsigned.bd | 14 + ...t1160_iled_blinky_cm7_xip_mdk_unsigned.bin | Bin 0 -> 14644 bytes ...1160_iled_blinky_cm7_xip_mdk_unsigned.srec | 662 +++ .../data/hab/evkmimxrt1170_flashloader.bd | 15 + .../data/hab/evkmimxrt1170_flashloader.bin | Bin 0 -> 82431 bytes .../data/hab/evkmimxrt1170_flashloader.srec | 5138 +++++++++++++++++ ...170_iled_blinky_cm7_QSPI_FLASH_unsigned.bd | 14 + ...70_iled_blinky_cm7_QSPI_FLASH_unsigned.bin | Bin 0 -> 26740 bytes ...70_iled_blinky_cm7_QSPI_FLASH_unsigned.s19 | 1418 +++++ ...led_blinky_cm7_int_RAM_non_xip_unsigned.bd | 14 + ...ed_blinky_cm7_int_RAM_non_xip_unsigned.bin | Bin 0 -> 25708 bytes ...ed_blinky_cm7_int_RAM_non_xip_unsigned.s19 | 1418 +++++ ...rt1170_iled_blinky_cm7_int_RAM_unsigned.bd | 15 + ...t1170_iled_blinky_cm7_int_RAM_unsigned.bin | Bin 0 -> 26732 bytes ...t1170_iled_blinky_cm7_int_RAM_unsigned.s19 | 1418 +++++ .../app.bin | Bin 0 -> 22636 bytes .../bdt.bin | Bin 0 -> 12 bytes .../ivt.bin | Bin 0 -> 32 bytes .../data/iee/aes_ctr128/iee_config.yaml | 8 +- .../data/iee/aes_ctr256/iee_config.yaml | 8 +- .../data/iee/aes_xts256/iee_config.yaml | 8 +- .../data/iee/aes_xts512/iee_config.yaml | 8 +- .../iee/aes_xts512_multiple/iee_config.yaml | 8 +- .../blink_fspi2_xip_cm33_ahab.bin | Bin 0 -> 25028 bytes .../iee/aes_xts512_rt1180/encrypted_blob.bin | Bin 0 -> 25040 bytes .../iee/aes_xts512_rt1180/iee_config.yaml | 32 + .../data/mbi/ext_xip_signed_lpc55s3x.yml | 16 + .../mbi/ext_xip_signed_lpc55s3x_invalid.yml | 17 + .../data/mbi/ext_xip_signed_rtxxxx.yml | 14 + .../mbi/ext_xip_signed_rtxxxx_invalid.yml | 15 + .../data/mbi/int_xip_signed_kw45xx.yml | 14 + .../mbi/int_xip_signed_kw45xx_invalid.yml | 15 + .../nxpimage/data/mbi/int_xip_signed_xip.yml | 13 + .../data/mbi/int_xip_signed_xip_invalid.yml | 14 + .../keys_and_certs/ec_pk_secp256r1_cert0.pem | 8 + .../mbi/keys_and_certs/ec_secp256r1_cert0.pem | 15 + .../mbi/keys_and_certs}/k0_cert0_2048.pem | 0 .../mbi/keys_and_certs}/k1_cert0_2048.pem | 0 .../root_k0_signed_cert0_noca.der.cert | Bin 0 -> 1117 bytes tests/nxpimage/data/mbi/test_application.bin | Bin 0 -> 16704 bytes ...inky_cm7_QSPI_FLASH_bootable_nopadding.bin | Bin 0 -> 26740 bytes tests/nxpimage/data/otfad/otfad_rt1160.yaml | 26 + .../nxpimage/data/otfad/otfad_rt1160_out.bin | Bin 0 -> 31232 bytes tests/nxpimage/data/otfad/otfad_rt1170.yaml | 26 + .../nxpimage/data/otfad/otfad_rt1170_out.bin | Bin 0 -> 31232 bytes tests/nxpimage/data/otfad/otfad_rt1180.yaml | 10 +- .../data/otfad/otfad_rt1180_scramble.yaml | 12 +- .../nxpimage/data/otfad/otfad_rt1180_txt.yaml | 12 +- tests/nxpimage/data/otfad/otfad_rt5xx.yaml | 4 + tests/nxpimage/data/otfad/otfad_rt6xx.yaml | 4 + .../data/utils/binary/binary_merge.yaml | 6 +- .../utils/binary/invalid_offset_merge.yaml | 29 + .../data/utils/binary/invalid_size_merge.yaml | 29 + .../data/utils/binary/secure_address_app.hex | 5 + .../data/utils/binary/signed_merge.bin | Bin 0 -> 80 bytes .../data/utils/binary/signed_merge.yaml | 29 + .../data/xmcd/rt116x/flexspi_ram_full.bin | Bin 0 -> 72 bytes .../data/xmcd/rt116x/flexspi_ram_full.yaml | 90 + ...ified.bin => flexspi_ram_simplified_0.bin} | Bin ...ied.yaml => flexspi_ram_simplified_0.yaml} | 6 +- .../xmcd/rt116x/flexspi_ram_simplified_1.bin | Bin 0 -> 12 bytes .../xmcd/rt116x/flexspi_ram_simplified_1.yaml | 38 + .../data/xmcd/rt116x/semc_sdram_full.yaml | 4 + .../xmcd/rt116x/semc_sdram_simplified.yaml | 4 + .../xmcd_rt116x_flexspi_ram_full.yaml | 362 ++ .../xmcd_rt116x_flexspi_ram_simplified.yaml | 37 + .../xmcd_rt116x_semc_sdram_full.yaml | 90 + .../xmcd_rt116x_semc_sdram_simplified.yaml | 32 + .../rt117x/flexspi_ram_full.bin} | Bin 512 -> 516 bytes .../data/xmcd/rt117x/flexspi_ram_full.yaml | 362 ++ .../xmcd/rt117x/flexspi_ram_simplified.yaml | 20 +- .../xmcd/rt117x/flexspi_ram_simplified_0.bin | Bin 0 -> 8 bytes .../xmcd/rt117x/flexspi_ram_simplified_0.yaml | 30 + .../xmcd/rt117x/flexspi_ram_simplified_1.bin | Bin 0 -> 12 bytes .../xmcd/rt117x/flexspi_ram_simplified_1.yaml | 38 + .../data/xmcd/rt117x/semc_sdram_full.yaml | 4 + .../xmcd/rt117x/semc_sdram_simplified.yaml | 4 + .../xmcd_rt117x_flexspi_ram_full.yaml | 362 ++ .../xmcd_rt117x_flexspi_ram_simplified.yaml | 37 + .../xmcd_rt117x_semc_sdram_full.yaml | 90 + .../xmcd_rt117x_semc_sdram_simplified.yaml | 32 + tests/nxpimage/test_nxpimage_ahab.py | 6 +- tests/nxpimage/test_nxpimage_bee.py | 1 - tests/nxpimage/test_nxpimage_bimg.py | 181 +- tests/nxpimage/test_nxpimage_binary.py | 92 +- tests/nxpimage/test_nxpimage_cert_block.py | 55 + tests/nxpimage/test_nxpimage_cfg_template.py | 4 +- tests/nxpimage/test_nxpimage_fcb.py | 4 +- tests/nxpimage/test_nxpimage_hab.py | 97 + tests/nxpimage/test_nxpimage_iee.py | 10 +- tests/nxpimage/test_nxpimage_mbi.py | 304 +- tests/nxpimage/test_nxpimage_otfad.py | 39 +- tests/nxpimage/test_nxpimage_sb31.py | 62 +- tests/nxpimage/test_nxpimage_xmcd.py | 71 +- tests/pfr/data/bad_dev.yml | 4 + tests/pfr/data/bad_rev.yml | 4 + tests/pfr/data/cfpa-lpc551x.yaml | 4 + tests/pfr/data/cfpa_after_reset.yml | 4 + tests/pfr/data/cfpa_lpc55s3x_default.yaml | 188 +- .../pfr/data/cfpa_lpc55s3x_default_full.yaml | 194 +- tests/pfr/data/cfpa_no_change.yml | 4 + tests/pfr/data/cfpa_pfrc.yml | 180 +- tests/pfr/data/cfpa_pfrc_lpc55s3x.yml | 10 +- tests/pfr/data/cfpa_test.yml | 4 + tests/pfr/data/cmpa_96mhz.json | 8 +- tests/pfr/data/cmpa_96mhz.yml | 131 +- tests/pfr/data/cmpa_96mhz_rotkh.yml | 130 +- .../fcb.bin => pfr/data/cmpa_lpc55s3x.bin} | Bin 512 -> 512 bytes tests/pfr/data/cmpa_lpc55s3x.json | 213 + tests/pfr/data/cmpa_lpc55s3x_default.yaml | 698 +-- .../pfr/data/cmpa_lpc55s3x_default_full.yaml | 702 +-- tests/pfr/data/cmpa_pfrc.yml | 314 +- tests/pfr/data/cmpa_pfrc_lpc55s3x.yml | 10 +- tests/pfr/data/database.yaml | 4 + tests/pfr/data/empty_yml.yml | 4 + tests/pfr/data/keys/IMG1_1_p256.pem | 5 + tests/pfr/data/keys/IMG1_1_p256.pub | 4 + tests/pfr/data/keys/IMG1_1_p384.pem | 6 + tests/pfr/data/keys/IMG1_1_p384.pub | 5 + tests/pfr/data/keys/IMG2_1_p256.pem | 5 + tests/pfr/data/keys/IMG2_1_p256.pub | 4 + tests/pfr/data/keys/IMG2_1_p384.pem | 6 + tests/pfr/data/keys/IMG2_1_p384.pub | 5 + tests/pfr/data/keys/IMG3_1_p256.pem | 5 + tests/pfr/data/keys/IMG3_1_p256.pub | 4 + tests/pfr/data/keys/IMG3_1_p384.pem | 6 + tests/pfr/data/keys/IMG3_1_p384.pub | 5 + tests/pfr/data/keys/IMG4_1_p256.pem | 5 + tests/pfr/data/keys/IMG4_1_p256.pub | 4 + tests/pfr/data/keys/IMG4_1_p384.pem | 6 + tests/pfr/data/keys/IMG4_1_p384.pub | 5 + tests/pfr/data/keys/ROT1_p256.pem | 5 + tests/pfr/data/keys/ROT1_p256.pub | 4 + tests/pfr/data/keys/ROT1_p384.pem | 6 + tests/pfr/data/keys/ROT1_p384.pub | 5 + tests/pfr/data/keys/ROT2_p256.pem | 5 + tests/pfr/data/keys/ROT2_p256.pub | 4 + tests/pfr/data/keys/ROT2_p384.pem | 6 + tests/pfr/data/keys/ROT2_p384.pub | 5 + tests/pfr/data/keys/ROT3_p256.pem | 5 + tests/pfr/data/keys/ROT3_p256.pub | 4 + tests/pfr/data/keys/ROT3_p384.pem | 6 + tests/pfr/data/keys/ROT3_p384.pub | 5 + tests/pfr/data/keys/ROT4_p256.pem | 5 + tests/pfr/data/keys/ROT4_p256.pub | 4 + tests/pfr/data/keys/ROT4_p384.pem | 6 + tests/pfr/data/keys/ROT4_p384.pub | 5 + tests/pfr/data/latest_rev.yml | 4 + tests/pfr/data/lpc55s6x_1b.xml | 300 +- tests/pfr/data/mbi_config_lpc55s3x.json | 20 + tests/pfr/test_pfr_cli.py | 12 + tests/sbfile/sb31/common.py | 3 +- tests/sbfile/sb31/test_functions.py | 41 +- tests/sbfile/test_backend_python.py | 2 +- tests/shadowregs/data/sh_regs_corrupted.yml | 4 + tests/shadowregs/data/sh_test_dev_x0.xml | 5 + tests/shadowregs/data/test_database.yaml | 4 + .../data/test_database_invalid_computed.yaml | 4 + tests/shadowregs/test_shadowregs.py | 35 +- tests/test_self.py | 2 +- tests/tp/audit_log/test_audit_log.py | 3 +- tests/tp/audit_log/test_audit_log_cli.py | 4 +- tests/tp/data/big_oem_cert_config.yaml | 4 + tests/tp/data/small_oem_cert_config.yaml | 4 + tests/tp/data_container/conftest.py | 3 +- .../data/certs_and_keys/k0_cert0_2048.pem | 28 + .../root_k0_signed_cert0_noca.der.cert | Bin 0 -> 1117 bytes .../root_k1_signed_cert0_noca.der.cert | Bin 0 -> 1117 bytes tests/utils/crypto/test_cert_blocks.py | 184 +- tests/utils/crypto/test_common.py | 3 - tests/utils/data/bad_format.xml | 5 + tests/utils/data/database.yaml | 4 + tests/utils/data/database_invalid.yaml | 4 + tests/utils/data/group_none_reg.yml | 4 + tests/utils/data/group_reg.yml | 4 + tests/utils/data/grp_regs.xml | 5 + tests/utils/data/images/image_0x80002000.elf | Bin 0 -> 1581984 bytes tests/utils/data/registers.xml | 5 + tests/utils/data/registers_corr.xml | 5 + tests/utils/data/registers_corr2.xml | 5 + tests/utils/data/registers_reserved.xml | 5 + tests/utils/test_binary_image.py | 46 +- tests/utils/test_database.py | 4 +- tests/utils/test_devicedescription.py | 1 - tests/utils/test_misc.py | 32 - tests/utils/test_nxpdevscan.py | 20 +- tests/utils/test_schema_validator.py | 193 +- tests/utils/test_usbfilter.py | 4 +- tools/approved_packages.json | 15 +- tools/checker_copyright_year.py | 154 +- tools/checker_dependencies.py | 7 +- tools/checker_py_headers.py | 103 + tools/fcb_header_to_xml.py | 4 +- tools/gitcov.py | 11 +- tools/release_notes.py | 4 +- tools/req_update.py | 10 +- tools/sample_config_data/tp_config_data.yaml | 4 + .../sample_config_data/tp_config_real_hw.yaml | 4 + tools/sample_config_data/tp_host_data.yaml | 4 + tools/sample_config_data/tp_host_real_hw.yaml | 4 + tools/sr_xls2xml.py | 5 +- tools/task_scheduler.py | 65 +- tools/tp_setup_workspace.py | 2 +- tox.ini | 2 +- 583 files changed, 25175 insertions(+), 8108 deletions(-) delete mode 100644 examples/dat/.gitignore delete mode 100644 examples/dat/README.md delete mode 100644 examples/dat/dck.pem delete mode 100644 examples/dat/dck.pub delete mode 100644 examples/dat/dck_rsa_2048.yml delete mode 100644 examples/dat/hsm/sahsm.py delete mode 100644 examples/dat/hsm/sasp.py delete mode 100644 examples/dat/p0_cert0_2048.pub delete mode 100644 examples/dat/p1_cert0_2048.pub create mode 100644 examples/data/cfpa_lpc55s6x_test.yaml delete mode 100644 examples/data/cfpa_test.json rename spsdk/data/ahab/{database.yml => database.yaml} (84%) rename spsdk/data/ahab/{sch_ahab.yml => sch_ahab.yaml} (97%) rename spsdk/data/ahab/{sch_signed_msg.yml => sch_signed_msg.yaml} (96%) rename spsdk/data/image/bootable_image/{database.yml => database.yaml} (60%) rename spsdk/data/image/bootable_image/{sch_bimg.yml => sch_bimg.yaml} (70%) rename spsdk/data/image/{database.yml => database.yaml} (99%) create mode 100644 spsdk/data/image/database_sb31.yaml rename spsdk/data/image/fcb/{database.yml => database.yaml} (63%) create mode 100644 spsdk/data/image/fcb/rt101x_flexspi_nor.xml create mode 100644 spsdk/data/image/fcb/rt102x_flexspi_nor.xml create mode 100644 spsdk/data/image/fcb/rt104x_flexspi_nor.xml create mode 100644 spsdk/data/image/fcb/rt116x_flexspi_nor.xml rename spsdk/data/image/fcb/{sch_fcb.yml => sch_fcb.yaml} (100%) rename spsdk/data/image/{sch_binary.yml => sch_binary.yaml} (89%) rename spsdk/data/image/{sch_mbimg.yml => sch_mbimg.yaml} (78%) rename spsdk/data/image/{sch_sb3.yml => sch_sb3.yaml} (98%) rename spsdk/data/image/{sch_tz.yml => sch_tz.yaml} (83%) rename spsdk/data/image/xmcd/{database.yml => database.yaml} (100%) rename spsdk/data/image/xmcd/{sch_xmcd.yml => sch_xmcd.yaml} (100%) rename spsdk/data/nxpcertgen/{certgen_config.yml => certgen_config.yaml} (96%) rename spsdk/data/nxpdebugmbox/{template_config.yml => template_config.yaml} (97%) rename spsdk/data/tp/{tpconfig_cfg_template.yml => tpconfig_cfg_template.yaml} (95%) rename spsdk/data/tp/{tphost_cfg_template.yml => tphost_cfg_template.yaml} (94%) rename spsdk/data/utils/bee/{sch_bee.yml => sch_bee.yaml} (100%) rename spsdk/data/utils/iee/{database.yml => database.yaml} (68%) rename spsdk/data/utils/iee/{sch_iee.yml => sch_iee.yaml} (72%) rename spsdk/data/utils/otfad/{database.yml => database.yaml} (50%) create mode 100644 spsdk/data/utils/otfad/fuses_rt117x.xml rename spsdk/data/utils/otfad/{sch_otfad.yml => sch_otfad.yaml} (89%) rename spsdk/data/utils/{sch_crypto.yml => sch_crypto.yaml} (67%) create mode 100644 spsdk/image/hab/__init__.py create mode 100644 spsdk/image/hab/hab_container.py create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_256_256.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_256.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_384.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert_invalid.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384_cert.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_cert.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_cert.json create mode 100644 tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384_cert.json create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/cert_256_256.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/cert_384_256.bin create mode 100644 tests/elftosb/data/workspace/output_images/lpc55s3x/cert_384_384.bin rename tests/nxpimage/data/bootable_image/lpc55s3x/{ => flexspi_nor}/application.bin (100%) rename tests/nxpimage/data/bootable_image/lpc55s3x/{ => flexspi_nor}/config.yaml (76%) rename tests/nxpimage/data/bootable_image/lpc55s3x/{ => flexspi_nor}/fcb.bin (100%) rename tests/nxpimage/data/bootable_image/lpc55s3x/{ => flexspi_nor}/merged_image.bin (100%) create mode 100644 tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/config.yaml rename tests/nxpimage/data/bootable_image/{rt117x => rt101x/flexspi_nor}/fcb.bin (100%) rename tests/nxpimage/data/bootable_image/{rt117x/application.bin => rt101x/flexspi_nor/hab_container.bin} (81%) rename tests/nxpimage/data/bootable_image/{rt5xx => rt101x/flexspi_nor}/keyblob.bin (100%) rename tests/nxpimage/data/bootable_image/{rt117x => rt101x/flexspi_nor}/merged_image.bin (98%) create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/bee_header_0.bin create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/bee_header_1.bin create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/fcb.bin create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt105x/application.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt105x/bdi.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt105x/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/bee_header_0.bin create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/bee_header_1.bin create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/fcb.bin create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt105x/ivt.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt105x/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt106x/application.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt106x/bdi.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt106x/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/bee_header_0.bin create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/bee_header_1.bin create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/fcb.bin create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt106x/ivt.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt106x/merged_image.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/merged_image.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/fcb.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/hab_container.bin rename tests/nxpimage/data/bootable_image/{rt6xx => rt116x/flexspi_nor}/keyblob.bin (100%) rename tests/nxpimage/data/bootable_image/{rt5xx => rt116x/flexspi_nor}/keystore.bin (100%) create mode 100644 tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/merged_image.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/semc_nand/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt116x/semc_nand/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt116x/semc_nand/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt117x/bdi.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt117x/config.yaml delete mode 100644 tests/nxpimage/data/bootable_image/rt117x/fcb.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nand/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nand/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nand/merged_image.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/fcb.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/keyblob.bin rename tests/nxpimage/data/bootable_image/{rt6xx => rt117x/flexspi_nor}/keystore.bin (100%) create mode 100644 tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt117x/ivt.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/semc_nand/config.yaml create mode 100644 tests/nxpimage/data/bootable_image/rt117x/semc_nand/hab_container.bin create mode 100644 tests/nxpimage/data/bootable_image/rt117x/semc_nand/merged_image.bin delete mode 100644 tests/nxpimage/data/bootable_image/rt117x/xmcd.bin rename tests/nxpimage/data/bootable_image/rt118x/{ => flexspi_nor}/ahab_container.bin (100%) rename tests/nxpimage/data/bootable_image/rt118x/{ => flexspi_nor}/config.yaml (94%) rename tests/nxpimage/data/bootable_image/rt118x/{ => flexspi_nor}/fcb.bin (100%) rename tests/nxpimage/data/bootable_image/rt118x/{ => flexspi_nor}/merged_image.bin (100%) rename tests/nxpimage/data/bootable_image/rt5xx/{ => flexspi_nor}/application.bin (100%) rename tests/nxpimage/data/bootable_image/{rt6xx => rt5xx/flexspi_nor}/config.yaml (66%) rename tests/nxpimage/data/bootable_image/rt5xx/{ => flexspi_nor}/fcb.bin (100%) create mode 100644 tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keyblob.bin create mode 100644 tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keystore.bin rename tests/nxpimage/data/bootable_image/rt5xx/{ => flexspi_nor}/merged_image.bin (100%) rename tests/nxpimage/data/bootable_image/rt6xx/{ => flexspi_nor}/application.bin (100%) rename tests/nxpimage/data/bootable_image/{rt5xx => rt6xx/flexspi_nor}/config.yaml (66%) rename tests/nxpimage/data/bootable_image/rt6xx/{ => flexspi_nor}/fcb.bin (100%) create mode 100644 tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keyblob.bin create mode 100644 tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keystore.bin rename tests/nxpimage/data/bootable_image/rt6xx/{ => flexspi_nor}/merged_image.bin (100%) create mode 100644 tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bd create mode 100644 tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bin create mode 100644 tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.srec create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bd create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_flashloader.srec create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.bd create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.s19 create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19 create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bd create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.s19 create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/app.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/bdt.bin create mode 100644 tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/ivt.bin create mode 100644 tests/nxpimage/data/iee/aes_xts512_rt1180/blink_fspi2_xip_cm33_ahab.bin create mode 100644 tests/nxpimage/data/iee/aes_xts512_rt1180/encrypted_blob.bin create mode 100644 tests/nxpimage/data/iee/aes_xts512_rt1180/iee_config.yaml create mode 100644 tests/nxpimage/data/mbi/ext_xip_signed_lpc55s3x.yml create mode 100644 tests/nxpimage/data/mbi/ext_xip_signed_lpc55s3x_invalid.yml create mode 100644 tests/nxpimage/data/mbi/ext_xip_signed_rtxxxx.yml create mode 100644 tests/nxpimage/data/mbi/ext_xip_signed_rtxxxx_invalid.yml create mode 100644 tests/nxpimage/data/mbi/int_xip_signed_kw45xx.yml create mode 100644 tests/nxpimage/data/mbi/int_xip_signed_kw45xx_invalid.yml create mode 100644 tests/nxpimage/data/mbi/int_xip_signed_xip.yml create mode 100644 tests/nxpimage/data/mbi/int_xip_signed_xip_invalid.yml create mode 100644 tests/nxpimage/data/mbi/keys_and_certs/ec_pk_secp256r1_cert0.pem create mode 100644 tests/nxpimage/data/mbi/keys_and_certs/ec_secp256r1_cert0.pem rename {examples/dat/hsm => tests/nxpimage/data/mbi/keys_and_certs}/k0_cert0_2048.pem (100%) rename {examples/dat/hsm => tests/nxpimage/data/mbi/keys_and_certs}/k1_cert0_2048.pem (100%) create mode 100644 tests/nxpimage/data/mbi/keys_and_certs/root_k0_signed_cert0_noca.der.cert create mode 100644 tests/nxpimage/data/mbi/test_application.bin create mode 100644 tests/nxpimage/data/otfad/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_bootable_nopadding.bin create mode 100644 tests/nxpimage/data/otfad/otfad_rt1160.yaml create mode 100644 tests/nxpimage/data/otfad/otfad_rt1160_out.bin create mode 100644 tests/nxpimage/data/otfad/otfad_rt1170.yaml create mode 100644 tests/nxpimage/data/otfad/otfad_rt1170_out.bin create mode 100644 tests/nxpimage/data/utils/binary/invalid_offset_merge.yaml create mode 100644 tests/nxpimage/data/utils/binary/invalid_size_merge.yaml create mode 100644 tests/nxpimage/data/utils/binary/secure_address_app.hex create mode 100644 tests/nxpimage/data/utils/binary/signed_merge.bin create mode 100644 tests/nxpimage/data/utils/binary/signed_merge.yaml create mode 100644 tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.bin create mode 100644 tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.yaml rename tests/nxpimage/data/xmcd/rt116x/{flexspi_ram_simplified.bin => flexspi_ram_simplified_0.bin} (100%) rename tests/nxpimage/data/xmcd/rt116x/{flexspi_ram_simplified.yaml => flexspi_ram_simplified_0.yaml} (93%) create mode 100644 tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_1.bin create mode 100644 tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_1.yaml create mode 100644 tests/nxpimage/data/xmcd/rt116x/templates/xmcd_rt116x_flexspi_ram_full.yaml create mode 100644 tests/nxpimage/data/xmcd/rt116x/templates/xmcd_rt116x_flexspi_ram_simplified.yaml create mode 100644 tests/nxpimage/data/xmcd/rt116x/templates/xmcd_rt116x_semc_sdram_full.yaml create mode 100644 tests/nxpimage/data/xmcd/rt116x/templates/xmcd_rt116x_semc_sdram_simplified.yaml rename tests/nxpimage/data/{bootable_image/rt105x/fcb.bin => xmcd/rt117x/flexspi_ram_full.bin} (87%) create mode 100644 tests/nxpimage/data/xmcd/rt117x/flexspi_ram_full.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/flexspi_ram_simplified_0.bin create mode 100644 tests/nxpimage/data/xmcd/rt117x/flexspi_ram_simplified_0.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/flexspi_ram_simplified_1.bin create mode 100644 tests/nxpimage/data/xmcd/rt117x/flexspi_ram_simplified_1.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/templates/xmcd_rt117x_flexspi_ram_full.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/templates/xmcd_rt117x_flexspi_ram_simplified.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/templates/xmcd_rt117x_semc_sdram_full.yaml create mode 100644 tests/nxpimage/data/xmcd/rt117x/templates/xmcd_rt117x_semc_sdram_simplified.yaml create mode 100644 tests/nxpimage/test_nxpimage_cert_block.py create mode 100644 tests/nxpimage/test_nxpimage_hab.py rename tests/{nxpimage/data/bootable_image/rt106x/fcb.bin => pfr/data/cmpa_lpc55s3x.bin} (75%) create mode 100644 tests/pfr/data/cmpa_lpc55s3x.json create mode 100644 tests/pfr/data/keys/IMG1_1_p256.pem create mode 100644 tests/pfr/data/keys/IMG1_1_p256.pub create mode 100644 tests/pfr/data/keys/IMG1_1_p384.pem create mode 100644 tests/pfr/data/keys/IMG1_1_p384.pub create mode 100644 tests/pfr/data/keys/IMG2_1_p256.pem create mode 100644 tests/pfr/data/keys/IMG2_1_p256.pub create mode 100644 tests/pfr/data/keys/IMG2_1_p384.pem create mode 100644 tests/pfr/data/keys/IMG2_1_p384.pub create mode 100644 tests/pfr/data/keys/IMG3_1_p256.pem create mode 100644 tests/pfr/data/keys/IMG3_1_p256.pub create mode 100644 tests/pfr/data/keys/IMG3_1_p384.pem create mode 100644 tests/pfr/data/keys/IMG3_1_p384.pub create mode 100644 tests/pfr/data/keys/IMG4_1_p256.pem create mode 100644 tests/pfr/data/keys/IMG4_1_p256.pub create mode 100644 tests/pfr/data/keys/IMG4_1_p384.pem create mode 100644 tests/pfr/data/keys/IMG4_1_p384.pub create mode 100644 tests/pfr/data/keys/ROT1_p256.pem create mode 100644 tests/pfr/data/keys/ROT1_p256.pub create mode 100644 tests/pfr/data/keys/ROT1_p384.pem create mode 100644 tests/pfr/data/keys/ROT1_p384.pub create mode 100644 tests/pfr/data/keys/ROT2_p256.pem create mode 100644 tests/pfr/data/keys/ROT2_p256.pub create mode 100644 tests/pfr/data/keys/ROT2_p384.pem create mode 100644 tests/pfr/data/keys/ROT2_p384.pub create mode 100644 tests/pfr/data/keys/ROT3_p256.pem create mode 100644 tests/pfr/data/keys/ROT3_p256.pub create mode 100644 tests/pfr/data/keys/ROT3_p384.pem create mode 100644 tests/pfr/data/keys/ROT3_p384.pub create mode 100644 tests/pfr/data/keys/ROT4_p256.pem create mode 100644 tests/pfr/data/keys/ROT4_p256.pub create mode 100644 tests/pfr/data/keys/ROT4_p384.pem create mode 100644 tests/pfr/data/keys/ROT4_p384.pub create mode 100644 tests/pfr/data/mbi_config_lpc55s3x.json create mode 100644 tests/utils/crypto/data/certs_and_keys/k0_cert0_2048.pem create mode 100644 tests/utils/crypto/data/certs_and_keys/root_k0_signed_cert0_noca.der.cert create mode 100644 tests/utils/crypto/data/certs_and_keys/root_k1_signed_cert0_noca.der.cert create mode 100644 tests/utils/data/images/image_0x80002000.elf create mode 100644 tools/checker_py_headers.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 22d961b7..82b7f8f0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.9.1 +current_version = 1.10.0 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P.*))? serialize = {major}.{minor}.{patch}.{suffix} diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 55c6a94c..471bc02e 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -4,51 +4,49 @@ name: Python Continous Integration on: push: - branches: [ "master" ] + branches: ["master"] pull_request: - branches: [ "master" ] + branches: ["master"] permissions: contents: read jobs: codecheck: - runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest,windows-latest,macos-latest] - python-version: ['3.7','3.11'] + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.8", "3.11"] name: Python ${{ matrix.python-version }} on ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set-up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4.3.0 - with: - python-version: ${{ matrix.python-version }} - architecture: 'x64' - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-develop.txt - - - name: Install SPSDK - run: | - python setup.py install - - - name: Run Codecheck - run: | - python codecheck.py -s --output reports_codecheck --info-check gitcov - - - name: Archive Results - uses: actions/upload-artifact@v3 - with: - name: Results-${{ matrix.python-version }}-${{ matrix.os }} - path: reports_codecheck - if: always() && job.status == 'failure' - + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set-up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4.3.0 + with: + python-version: ${{ matrix.python-version }} + architecture: "x64" + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements-develop.txt + + - name: Install SPSDK + run: | + python setup.py install + + - name: Run Codecheck + run: | + python codecheck.py -s --output reports_codecheck --info-check gitcov + + - name: Archive Results + uses: actions/upload-artifact@v3 + with: + name: Results-${{ matrix.python-version }}-${{ matrix.os }} + path: reports_codecheck + if: always() && job.status == 'failure' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e37db4a5..9e023ecc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,10 +8,10 @@ repos: entry: python tools/checker_copyright_year.py language: system - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.8.0 + rev: 5.12.0 hooks: - id: isort diff --git a/.pylintrc b/.pylintrc index 8aa2fbb1..cbdec543 100644 --- a/.pylintrc +++ b/.pylintrc @@ -85,7 +85,7 @@ persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. -py-version=3.7 +py-version=3.8 # Discover python modules and packages in the file system subtree. recursive=no diff --git a/.readthedocs.yml b/.readthedocs.yml index b5e377e0..c9e74532 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,7 +11,7 @@ sphinx: # Optionally set the version of Python and requirements required to build your docs python: - version: "3.7" + version: "3.8" install: - method: pip path: .[tp] diff --git a/SW_Content_Register_SPSDK.txt b/SW_Content_Register_SPSDK.txt index 786f58c0..afbfe9ec 100644 --- a/SW_Content_Register_SPSDK.txt +++ b/SW_Content_Register_SPSDK.txt @@ -1,7 +1,7 @@ NXP Software Content Register Package: NXP SPSDK -Version: 1.9.1 +Version: 1.10.0 Outgoing License: BSD-3-Clause License Files: LICENSE Type of content: Source code diff --git a/codecheck.py b/codecheck.py index 3e3dad79..a33c91b3 100644 --- a/codecheck.py +++ b/codecheck.py @@ -20,10 +20,13 @@ import prettytable from tools import gitcov +from tools.checker_copyright_year import COPYRIGHT_EXTENSIONS, fix_copyright_in_files from tools.task_scheduler import PrettyProcessRunner, TaskInfo, TaskList, TaskResult OUTPUT_FOLDER = "reports" CPU_CNT = os.cpu_count() or 1 + + CHECK_LIST = [ "PYTEST", "GITCOV", @@ -41,12 +44,13 @@ "BLACK", "ISORT", "COPYRIGHT", + "PY_HEADERS", ] log = logging.getLogger(__name__) colorama.init() -def print_results(tasks: List[TaskInfo], info_checks: Optional[List[str]] = None) -> None: +def print_results(tasks: List[TaskInfo]) -> None: """Print Code Check results in table.""" table = prettytable.PrettyTable(["#", "Test", "Result", "Exec Time", "Error count", "Log"]) table.align = "l" @@ -55,25 +59,14 @@ def print_results(tasks: List[TaskInfo], info_checks: Optional[List[str]] = None table.hrules = prettytable.HEADER table.vrules = prettytable.NONE - result_colors = { - "PASS": colorama.Fore.GREEN, - "FAILED": colorama.Fore.RED, - "INFO": colorama.Fore.CYAN, - } - for i, task in enumerate(tasks, start=1): - result_text = "FAILED" assert task.result - if task.result.error_count == 0: - result_text = "PASS" - if info_checks and task.name in info_checks: - result_text = "INFO" table.add_row( [ colorama.Fore.YELLOW + str(i), colorama.Fore.WHITE + task.name, - result_colors[result_text] + result_text, + task.status_str(), colorama.Fore.WHITE + task.get_exec_time(), colorama.Fore.CYAN + str(task.result.error_count), colorama.Fore.BLUE + task.result.output_log, @@ -83,12 +76,11 @@ def print_results(tasks: List[TaskInfo], info_checks: Optional[List[str]] = None click.echo(colorama.Style.RESET_ALL) -def check_results(tasks: List[TaskInfo], info_check: List[str], output: str = "reports") -> int: +def check_results(tasks: List[TaskInfo], output: str = "reports") -> int: """Print Code Check results in table.""" ret = 0 for task in tasks: - err_cnt = task.result.error_count if task.result else -1 output_log: List[str] = [] if task.exception: @@ -98,9 +90,8 @@ def check_results(tasks: List[TaskInfo], info_check: List[str], output: str = "r f.write(str(task.exception)) output_log.append(exc_log) - if not task.result or err_cnt != 0: - if not info_check or (info_check and task.name not in info_check): - ret = 1 + if not task.result or (err_cnt != 0 and not task.info_only): + ret = 1 if task.result: res_log = task.result.output_log @@ -254,7 +245,9 @@ def check_copyright_year(output: str = OUTPUT_FOLDER) -> TaskResult: """Check the project against copy right year rules.""" output_log = os.path.join(output, "copyright_year.txt") res = 0 - changed_files = gitcov.get_changed_files(repo_path=".", include_merges=True) + changed_files = gitcov.get_changed_files( + repo_path=".", include_merges=True, file_extensions=COPYRIGHT_EXTENSIONS + ) with open(output_log, "w", encoding="utf-8") as f: res = subprocess.call( f"python tools/checker_copyright_year.py {' '.join(changed_files)}".split(), @@ -269,6 +262,66 @@ def check_copyright_year(output: str = OUTPUT_FOLDER) -> TaskResult: return TaskResult(error_count=res, output_log=output_log) +def check_py_file_headers(output: str = OUTPUT_FOLDER) -> TaskResult: + """Check that python files have valid header.""" + output_log = os.path.join(output, "py_header.txt") + res = 0 + changed_files = gitcov.get_changed_files( + repo_path=".", include_merges=True, file_extensions=COPYRIGHT_EXTENSIONS + ) + with open(output_log, "w", encoding="utf-8") as f: + res = subprocess.call( + f"python tools/checker_py_headers.py {' '.join(changed_files)}".split(), + stdout=f, + stderr=f, + ) + + if res: + with open(output_log, "r", encoding="utf-8") as f: + res = len(f.read().splitlines()) + + return TaskResult(error_count=res, output_log=output_log) + + +def fix_copyight_year() -> None: + """Find all changed files and fix the copyright year in them.""" + changed_files = gitcov.get_changed_files( + repo_path=".", include_merges=True, file_extensions=COPYRIGHT_EXTENSIONS + ) + fix_copyright_in_files(changed_files) + + +def fix_py_file_headers() -> None: + changed_files = gitcov.get_changed_files( + repo_path=".", include_merges=True, file_extensions=COPYRIGHT_EXTENSIONS + ) + subprocess.call( + f"python tools/checker_py_headers.py --fix {' '.join(changed_files)}".split(), + ) + + +def fix_found_problems(checks: TaskList, silence: int = 0, run_check_again: bool = True) -> None: + """Fix the failed checks automatically is possible.""" + re_checks = TaskList() + fixers = {"COPYRIGHT": fix_copyight_year, "PY_HEADERS": fix_py_file_headers} + for check in checks: + if check.name not in fixers: + continue + if check.result and check.result.error_count != 0: + fixers[check.name]() + click.echo(f"{colorama.Fore.GREEN}{check.name} problems fixed .{colorama.Fore.RESET}") + check.reset() + re_checks.append(check) + if run_check_again and len(re_checks) > 0: + click.echo("Running the failed codechecks again.") + runner = PrettyProcessRunner( + re_checks, print_func=(lambda x: None) if silence else click.echo + ) + runner.run(CPU_CNT, True) + if silence < 2: + print_results(re_checks) + + @click.command(no_args_is_help=False) @click.option( "-c", @@ -311,37 +364,50 @@ def check_copyright_year(output: str = OUTPUT_FOLDER) -> TaskResult: default="reports", help="Output folder to store reports files.", ) +@click.option( + "-f", + "--fix", + is_flag=True, + default=False, + help="Fix the problems automatically if possible.", +) def main( check: List[str], info_check: List[str], job_cnt: int, silence: int, output: click.Path, + fix: bool, ) -> None: """Simple tool to check the SPSDK development rules. Overall result is passed to OS. - - :param check: List of tests to run. - :param info_check: List of tests to run which don't affect the overall result. - :param silence: Level of silence 0: full print; 1: print only summary; 2: print nothing. - :param job_cnt: Select count of concurrent tests. - :param output: Output folder for reports. """ # logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) logging.basicConfig(level=logging.INFO) output_dir = str(output) if output else OUTPUT_FOLDER ret = 1 + # the baseline PYLINT_ALL, RADON_ALL, and RADON_C checkers are always just informative + info_check = [x.upper() for x in list(info_check)] try: available_checks = TaskList( [ - TaskInfo("PYTEST", check_pytest, output=output_dir), - TaskInfo("GITCOV", check_gitcov, output=output_dir, dependencies=["PYTEST"]), + TaskInfo( + "PYTEST", check_pytest, output=output_dir, info_only="PYTEST" in info_check + ), + TaskInfo( + "GITCOV", + check_gitcov, + output=output_dir, + dependencies=["PYTEST"], + info_only="GITCOV" in info_check, + ), TaskInfo( "PYLINT_ALL", check_pylint, args="spsdk examples tools codecheck.py", output_log=os.path.join(output_dir, "pylint_all.txt"), + info_only=True, ), TaskInfo( "PYLINT", @@ -349,6 +415,8 @@ def main( input_log=os.path.join(output_dir, "pylint_all.txt"), output_log=os.path.join(output_dir, "pylint.txt"), dependencies=["PYLINT_ALL"], + inherit_failure=False, + info_only="PYLINT" in info_check, ), # This is already covered by PYLINT # TaskInfo( @@ -362,23 +430,39 @@ def main( check_pylint, args="spsdk --rcfile pylint-doc-rules.ini", output_log=os.path.join(output_dir, "pylint_docs.txt"), + info_only="PYLINT_DOCS" in info_check, ), TaskInfo( "MYPY", check_mypy, args=["spsdk", "examples"], output_log=os.path.join(output_dir, "mypy.txt"), + info_only="MYPY" in info_check, ), TaskInfo( "MYPY_TOOLS", check_mypy, args=["tools", "codecheck.py"], output_log=os.path.join(output_dir, "mypy_tools.txt"), + info_only="MYPY_TOOLS" in info_check, + ), + TaskInfo( + "DEPENDENCIES", + check_dependencies, + output=output, + info_only="DEPENDENCIES" in info_check, ), - TaskInfo("DEPENDENCIES", check_dependencies, output=output), - TaskInfo("PYDOCSTYLE", check_pydocstyle, output=output_dir), TaskInfo( - "RADON_ALL", check_radon, output_log=os.path.join(output_dir, "radon_all.txt") + "PYDOCSTYLE", + check_pydocstyle, + output=output_dir, + info_only="PYDOCSTYLE" in info_check, + ), + TaskInfo( + "RADON_ALL", + check_radon, + output_log=os.path.join(output_dir, "radon_all.txt"), + info_only=True, ), TaskInfo( "RADON_C", @@ -387,6 +471,7 @@ def main( input_log=os.path.join(output_dir, "radon_all.txt"), output_log=os.path.join(output_dir, "radon_c.txt"), dependencies=["RADON_ALL"], + info_only=True, ), TaskInfo( "RADON_D", @@ -395,10 +480,32 @@ def main( input_log=os.path.join(output_dir, "radon_all.txt"), output_log=os.path.join(output_dir, "radon_d.txt"), dependencies=["RADON_ALL"], + info_only="RADON_D" in info_check, + ), + TaskInfo( + "BLACK", + check_black, + output=output_dir, + info_only="BLACK" in info_check, + ), + TaskInfo( + "ISORT", + check_isort, + output=output_dir, + info_only="ISORT" in info_check, + ), + TaskInfo( + "COPYRIGHT", + check_copyright_year, + output=output_dir, + info_only="COPYRIGHT" in info_check, + ), + TaskInfo( + "PY_HEADERS", + check_py_file_headers, + output=output_dir, + info_only="PY_HEADERS" in info_check, ), - TaskInfo("BLACK", check_black, output=output_dir), - TaskInfo("ISORT", check_isort, output=output_dir), - TaskInfo("COPYRIGHT", check_copyright_year, output=output_dir), ] ) checks = TaskList() @@ -415,29 +522,28 @@ def main( checks.append(extra_task) checks.append(task) - # the baseline PYLINT_ALL, RADON_ALL, and RADON_C checkers are always just informative - info_check = list(info_check) - info_check.append("PYLINT_ALL") - info_check.append("RADON_ALL") - info_check.append("RADON_C") - if not os.path.isdir(output_dir): os.mkdir(output_dir) runner = PrettyProcessRunner(checks, print_func=(lambda x: None) if silence else click.echo) runner.run(job_cnt, True) - ret = check_results(checks, info_check, output_dir) + ret = check_results(checks, output_dir) if silence < 2: - print_results(checks, info_check) + print_results(checks) click.echo(f"Overall time: {round(runner.process_time, 1)} second(s).") click.echo( f"Overall result: {(colorama.Fore.GREEN+'PASS') if ret == 0 else (colorama.Fore.RED+'FAILED')}." ) + if fix: + fix_found_problems(checks, silence=silence) + ret = 0 + except Exception as exc: # pylint: disable=broad-except click.echo(exc) ret = 1 + sys.exit(ret) diff --git a/docs/apps/blhost.rst b/docs/apps/blhost.rst index 04d0cb59..5e30c204 100644 --- a/docs/apps/blhost.rst +++ b/docs/apps/blhost.rst @@ -441,3 +441,7 @@ After the reset the device boots from flash and user image is programmed success .. click:: spsdk.apps.blhost:update_life_cycle :prog: blhost update-life-cycle :nested: full + +.. click:: spsdk.apps.blhost:ele_message + :prog: blhost ele-message + :nested: full diff --git a/docs/apps/images.rst b/docs/apps/images.rst index b9118be4..f3eb32c1 100644 --- a/docs/apps/images.rst +++ b/docs/apps/images.rst @@ -33,7 +33,7 @@ Sample configuration for LPC55s6x plain signed XIP image. Other sample configura # == Trust Zone Settings == # enableTrustZone: false # TrustZone enable option, If not specified, the Trust zone is disabled. - trustZonePresetFile: my_tz_custom.yml # TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used. + trustZonePresetFile: my_tz_custom.yaml # TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used. # # == Certificate V2 Settings == # @@ -168,11 +168,11 @@ Example of use for parse binary AHAB container .. code-block:: yaml - # =========== Advanced High-Assurance Boot Configuration template for rt1180. =========== + # =========== Advanced High-Assurance Boot Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == # ---------------------------------------------------------------------------------------------------- - family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] + family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] output: generated_ahab.bin # [Required], Output AHAB file name, Revision of silicon containers: # [Required], List of containers present in AHAB., The order of containers in the list defines the order in AHAB. @@ -246,7 +246,7 @@ Example of use for parse binary AHAB container The full AHAB configuration template could be generated by nxpimage tool "get_template" sub-command for family that supports AHAB, example: -``nxpimage ahab get-template -f rt1180 ./my_config_templates`` +``nxpimage ahab get-template -f rt118x ./my_config_templates`` ------------------------------------------ Flash encryption engines diff --git a/docs/exts/generate_schemas.py b/docs/exts/generate_schemas.py index 1e2f2e69..942e4bf2 100644 --- a/docs/exts/generate_schemas.py +++ b/docs/exts/generate_schemas.py @@ -4,7 +4,7 @@ # Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause -# Script for the automated generation of schemas documentation for elftosb +# Script for the automated generation of schemas documentation for elftosb/nxpimage import os from typing import Any, Dict, List, Sequence @@ -93,15 +93,9 @@ def get_template(schemas: Dict, name: str) -> str: return yaml_data -def main(): - print("Running generate schemas script") - if os.path.exists(SCHEMAS_FILE): - os.remove(SCHEMAS_FILE) - print("Existing schemas file has been removed") - +def get_mbi_doc() -> None: + """Get doc for MBI classes.""" image_classes = get_all_mbi_classes() - image_classes.append(SecureBinary31) - for cls in image_classes: validation_schemas = cls.get_validation_schemas() schema = get_schema(validation_schemas) @@ -109,6 +103,28 @@ def main(): parsed_schema = parse_schema(schema) template = get_template([schema], f"YAML template {cls.__name__}") append_schema(parsed_schema, template) + + +def get_sb3_doc() -> None: + """Get doc for SB3 configurations.""" + families = SecureBinary31.get_supported_families() + for fam in families: + validation_schemas = SecureBinary31.get_validation_schemas(fam) + schema = get_schema(validation_schemas) + schema["title"] = f"{SecureBinary31.__name__} for {fam}" + parsed_schema = parse_schema(schema) + template = get_template([schema], f"YAML template {SecureBinary31.__name__} for {fam}") + append_schema(parsed_schema, template) + + +def main(): + print("Running generate schemas script") + if os.path.exists(SCHEMAS_FILE): + os.remove(SCHEMAS_FILE) + print("Existing schemas file has been removed") + + get_mbi_doc() + get_sb3_doc() print("Finished running") diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 7f2dd979..71c0f7c4 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -8,6 +8,39 @@ Release Notes ============= +----------------------- +1.10.0 (5-April-2023) +----------------------- + +**New features** + +* :ref:`blhost`: + - add new command: ele_message +* :ref:`nxpdebugmbox`: + - add command: read UUID from device + - update PyOCD to latest version to support CMSIS DAP FW v3 +* :ref:`nxpdevhsm`: + - USER_PCK rename to CUST_MK_SK +* :ref:`nxpimage`: + - add subcommand group for generate and parse certificate block + - replace private key to signature provider in master boot image + - OTFAD support for RT1170 +* :ref:`ifr`: + - add commands read/write +* :ref:`pfr`: + - add CMPA erase command + +**Bugfixes** + +* :ref:`nxpdebugmbox`: + - fix AP selection issue for PyOCD and PEMICRO + - fix DAC verification when there is only 1 root key +* :ref:`nxpimage`: + - fix MBI issue with HMAC +* :ref:`shadowregs`: + - fix endianness for OTP MASTER KEY +* drop support for Python 3.7 + ----------------------- 1.9.1 (17-March-2023) ----------------------- @@ -27,7 +60,7 @@ Release Notes * :ref:`nxpimage`: - fix handling exception when the root cert index is wrong * :ref:`tphost`/:ref:`tpconfig`: - - incorrect output in TP PG command in case of an failure + - Incorrect output in TP PG command in case of an failure ------------------------- 1.9.0 (30-January-2023) diff --git a/docs/spsdk.rst b/docs/spsdk.rst index 31245ee2..494e98cd 100644 --- a/docs/spsdk.rst +++ b/docs/spsdk.rst @@ -81,7 +81,7 @@ Supported OS Supported Environment ===================== -SPSDK is tested on *Python 3.7+* interpreter, old version 2.x is not supported. +SPSDK is tested on *Python 3.8+* interpreter, old version 2.x is not supported. =========== Versioning diff --git a/docs/usage/installation.rst b/docs/usage/installation.rst index 7e15f103..002de326 100644 --- a/docs/usage/installation.rst +++ b/docs/usage/installation.rst @@ -6,7 +6,7 @@ Installation Guide Requirements ------------ -- Make sure to have `Python 3.7+ `_ installed (old version 2.x is not supported). +- Make sure to have `Python 3.8+ `_ installed (old version 2.x is not supported). - Create and activate a virtual environment (``venv``, ``pipenv``, etc.) - Upgrade PyPI to the latest version - Install SPSDK diff --git a/examples/dat/.gitignore b/examples/dat/.gitignore deleted file mode 100644 index f59ec20a..00000000 --- a/examples/dat/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* \ No newline at end of file diff --git a/examples/dat/README.md b/examples/dat/README.md deleted file mode 100644 index e6d99f98..00000000 --- a/examples/dat/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Creating Debug Credential with the remote signing - -This example demonstrates how to use a custom remote signing service to sign Debug Credential file - -**Important files** - -Content of the `hsm` represents the remote side of things - -`hsm/sahsm.py` represents a remote signing service \ -`hsm/sasp.py` is the custom Signature Provider, a interface to the signing service: -- contains a class derived from `spsdk.crypto.SignatureProvider` -- the class has to implement: - - `sign(bytes) -> bytes` function which performs the actual signing - - `info() -> str` function returning some information about the signature provider (for debugging purposes) - - `sp_type (str)` class attribute that identifies the concrete implementation of SignatureProvider - -`dck_rsa_2048.yml` -- configuration file for `nxpkeygen gendc` command -- new configuration field `sign_provider`: - - format `"type=;=;=;..."` - - the `sp_type` has to match the sp_type class attribute defined earlier - - the remaining key-value pairs are passed to the `__init__` method of concrete Signature Provider - - e.g.: `"type=file;file_path=private_key.pem"` will instantiate `spsdk.crypto.PlainFileSP(file_path='private_key.pem')` -- new configuration field `rot_id`: - - due to the nature of creating Debug Credential file we need to know in advance which of the private keys will be used to perform the actual signing - - `rot_id` is a 0-based index representing the private key that will be used with respect to `rot_meta` - - e.g.: if we want to use a private key that corresponds to public key `p1_cert0_2048.pub`, `rot_id` has to be set to `1` - -**How to run this example**: -1) install two additional dependencies: - - `pip install flask requests` -2) run the custom HSM (a flask application) in a separate shell: - - `python hsm\sahsm.py` -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) \ No newline at end of file diff --git a/examples/dat/dck.pem b/examples/dat/dck.pem deleted file mode 100644 index e0ee1770..00000000 --- a/examples/dat/dck.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDWF7joK7GSspr4 -/427i3v3ooDIMktANAdEIMrYag5k+PGkp8FN9ktwMD3iFjIsPdY0qRdq7Pas3Vil -jHJe5J5KjDUh/Fbke1nIirho62VREOJ7SRHfkbAf0HTCmaCP8yzf7ymUZu1QeaOz -nQZJc/ksdT4HPHIKiwsbv5gm6xh9RxGKxqWjHnl7noXswXG8DpdWANZ7s2TZh2uH -Sy83s/OPWWbrLOTGiBDvLJGAtxd1O6fMg3QcG1pFlljTk9NfcuLa0JM94UHRwBGv -ypL+C0oHsbWS6Pa607lkEZDziQTWbjl+xsTI4/Vo2lPnL06lLLeggkI1/MeLkmI6 -pauWkN7JAgMBAAECggEBAMJbttnPEN4ElvwFxD3GWzxsok0cm4Qlt5L50iy122t/ -NKAuSybjuYiXwxZE4DeZy6Ty/PdtOGOmbpub7RiIo8s5jeLUmm3CLbgJd3CIguod -Uf5qT3ePYzz+mYEwxrroII0LKcw3xKmSRDMDEnoMduENbspS7ZT0tZjzTsXGF+1h -tlGdMauNvMGhrF+1izs0GKUMS+jg4dTaMusM36PAmVo9ScC8eVr5nfyeKs45hTNY -1DLJawtzsJg7KQpDc3cSdqv9TRcP+MufZ77UAddSo9LHDfrAdAGXo8Meea1IYd9z -IsP49eRwknoA5r646kq9WXZWO4cVuRUCw9g2XQf/vIECgYEA6mZj0brag8d21DPy -raSLTxbQS5IzNz2UZDSVA1//OhkVGtwIC8K9+40aTTzAIwIKQgMPQxuVk9WaQpVr -CBunhqwRvWIczsWK5zxh+yu1svIpOsvKqw9GcINx1oo3Ann6ztcx0yMHpo1t/Ixg -Tq7taL0olv1vNn4LVrp3+pbTNeUCgYEA6dJF97QVYoQS18C3cwYQQpi8VAU5f0Tg -peKHlgiEGRNO2P5I2Dm3dPKPHpag/LmPnSg6rkdnZaiyYgSNHGND86Xu9BAIiaXV -mDKBi+WpFehbwvQ38hgR/qaT+C7/8xzZ2TBlKFJtValkjvuqk5WOW8fZF8Q76m+v -ExhinFFwdxUCgYBZedZWxAYP0b9Zh672e941xZajkl58ksQncC8Fx8pMqjB1PVLQ -8njFInrMywjpFClIQ5S64De75Ajrq8/cm3nanapz6ZTnlV7NoC4iy++jLG9yo0Jt -A/q2jmaRJ9eR2XBVLq41U6Ti/g/jHM7SmlHD7pkk8f9XBMxdQfb0sEhVxQKBgQCh -TYEdJ6uVAd3p3zXu8ODowGszX0NSDBcZ+RNmKkvO7KX5RjrEh9SKpgabqHg4wF5V -oXoR3opy9BTTT6Z1TfmDrRYSYrkxFCkszLLX6sBhr6EV9dpedcJ8NThg/zNs6JtU -nHE8htiMPsZzaSQ8mfDSWiTmo2ZeDVvuxWTJx9jZHQKBgQDQNbatBjj+f28Z3Uap -FrqZu9He5TW6/qU/tPaEi9yaLqgVttSUvVaqKgnJKHD+2S2w6BN+HyMH0gQ+x1xI -MaCpejDkVPfeQJBxy1vlayhKcRl2i9DqqSWADoSVaJfcwHee6DFKoYnmAZeSdGXy -26JdSuCDnDNIRkC903uqVk3YYQ== ------END PRIVATE KEY----- diff --git a/examples/dat/dck.pub b/examples/dat/dck.pub deleted file mode 100644 index 50ead93f..00000000 --- a/examples/dat/dck.pub +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEA1he46CuxkrKa+P+Nu4t796KAyDJLQDQHRCDK2GoOZPjxpKfBTfZL -cDA94hYyLD3WNKkXauz2rN1YpYxyXuSeSow1IfxW5HtZyIq4aOtlURDie0kR35Gw -H9B0wpmgj/Ms3+8plGbtUHmjs50GSXP5LHU+BzxyCosLG7+YJusYfUcRisalox55 -e56F7MFxvA6XVgDWe7Nk2Ydrh0svN7Pzj1lm6yzkxogQ7yyRgLcXdTunzIN0HBta -RZZY05PTX3Li2tCTPeFB0cARr8qS/gtKB7G1kuj2utO5ZBGQ84kE1m45fsbEyOP1 -aNpT5y9OpSy3oIJCNfzHi5JiOqWrlpDeyQIDAQAB ------END RSA PUBLIC KEY----- diff --git a/examples/dat/dck_rsa_2048.yml b/examples/dat/dck_rsa_2048.yml deleted file mode 100644 index 1d02f0a8..00000000 --- a/examples/dat/dck_rsa_2048.yml +++ /dev/null @@ -1,116 +0,0 @@ -# DC Block structure -# ============================================ -# ============================================ -# ============================================ -# === Version === -# ============================================ -# === Soc Class === -# ============================================ -# === UUID === -# ============================================ -# === RoT Meta SHA256 of following: === -# === RoT Key0 SHA256 === -# === RoT Key1 SHA256 === -# === RoT Key2 SHA256 === -# === RoT Key3 SHA256 === -# ============================================ -# === Debugger Key DCK (Pub): === -# === Mod: 2048 Exp: 32 === -# ============================================ -# === CC SOCU === -# ============================================ -# === CC VU === -# ============================================ -# === CB === -# ============================================ -# === RoT Key (pub) === -# === Mod: 2048 Exp: 32 === -# ============================================ -# ============================================ -# === Signature of all block === -# === SHA256 of whole block => RSA(RoTK) === -# ============================================ -# ============================================ -# ============================================ - -# ============ SoC Class ============ -# A unique identifier for a set of SoCs that require no SoC-specific differentiation in -# their debug authentication. The main usage is to allow a different set of debug -# domains and options to be negotiated between the device configuration and -# credentials. A class can contain just a single revision of a single SoC model, if the -# granularity of debug control warrants it. -# Examples list of possible settings: -# 0x0001: LPC550x, LPC55s0x, LPC551x, LPC55s1x, LPC552x, LPC55s2x, LPC55s3x, LPC55s6x - -socc: 0x0001 - -# ============ Device UUID ============ -# 128-bit IETF RFC4122 compliant non-sequential Universally Unique Identifier (UUID) -uuid: "E004090E6BDD2155BBCE9E0665805BE3" - -# ============ SoC Usage ============ -# A CC (constraint) value that is a bit mask, and whose bits are used in an -# SoCC-specific manner. These bits are typically used for controlling which debug -# domains are accessed via the authentication protocol, but device-specific debug -# options can be managed in this way also. -cc_socu: 0x03FF - -# ============ Vendor Usage ============ -# A CC (constraint) value that is opaque to the debug authentication protocol itself but -# which can be leveraged by vendors in product-specific ways. -cc_vu: 0x5678 - -# ============ Credential Beacon & Authentication beacon ============ -# A value that is passed through the authentication protocol, which is not interpreted -# by the protocol but is instead made visible to the application being debugged. A -# credential beacon is associated with a DC and is therefore vendor/RoT-signed. An -# authentication beacon is provided and signed by the debugger during the -# authentication process. -cc_beacon: 0 - -# ============ RoT meta-data ============ -# The RoT meta-data required by the device to corroborate; the ROTID sent in the -# DAC, the field in this DC, and any additional RoT state that is not stored within the -# device. This allows different RoT identification, management and revocation -# solutions to be handled. -rot_meta: - - ./p0_cert0_2048.pub - - ./p1_cert0_2048.pub - -# ============ RoT Identifier ============ -# RoTID allows the debugger to infer which RoT public key(s) are acceptable to the -# device. If the debugger cannot or does not provide such a credential, the -# authentication process will fail. -rot_id: 0 - -# ============ Debug Credential Key ============ -# A user-owned key pair. The public part of the key is associated with a DC, the -# private part is held by the user and used to produce signatures during -# authentication. -dck: ./dck.pub - -# ================================================================================================== -# Signature configuration area -# ================================================================================================== -# There are two ways how sign the final DC data blob. -# -# 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style -# to use it by rotk key. As a second way to do same is use sign_provider option with 'type=file'. -# -# 2. For case that Debug Credential files are generated in untrusted environment (without access to RoT private keys), -# there is option to use plugin (example how to create own plugin is in: ./SPSDK/examples/dat/hsm/). The plugin -# has simple interface that allows handle DC data blob into plugin with index of RoT meta public key to get back signed -# DC image. -# -# Those options are exclusive, so only one option could be used to sign the DC. - -# ============ Signature Provider ============ -# To use signing provider examples -# -# sign_provider: 'type=file;file_path=./hsm/k0_cert0_2048.pem' -# sign_provider: : 'type=sasp;key_number=0' -sign_provider: "type=sasp;key_number=0" -# ============ RoT signature private key ============ -# Private key for for the RoT meta chosen by rot_id to sign the image. -# rotk: rotk0.pem - diff --git a/examples/dat/hsm/sahsm.py b/examples/dat/hsm/sahsm.py deleted file mode 100644 index 5c4fbdef..00000000 --- a/examples/dat/hsm/sahsm.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 2020-2021 NXP -# -# SPDX-License-Identifier: BSD-3-Clause - -"""Module represent a customer-specific HSM system.""" -import base64 -import os - -from flask import Flask, Response, jsonify, request - -from spsdk import crypto - -APP = Flask(__name__) -THIS_DIR = os.path.dirname(__file__) - - -@APP.route("/signer/", methods=["GET"]) -def signer(num: int) -> Response: - """Route (API) that performing the signing. - - :param num: Index of the key to use (rot_id) - :return: Signature wrapped in json, encoded in base64 - """ - private_key_file = os.path.join(THIS_DIR, f"k{num}_cert0_2048.pem") - private_key = crypto.load_private_key(private_key_file) - - # in this example we assume RSA keys - assert isinstance(private_key, crypto.RSAPrivateKey) - - data_to_sign = base64.b64decode(request.args["data"]) - signature = private_key.sign( - data=data_to_sign, - padding=crypto.padding.PKCS1v15(), - algorithm=crypto.hashes.SHA256(), - ) - data = base64.b64encode(signature) - return jsonify({"signature": data.decode("utf-8")}) - - -if __name__ == "__main__": - APP.run(debug=True) diff --git a/examples/dat/hsm/sasp.py b/examples/dat/hsm/sasp.py deleted file mode 100644 index e527c412..00000000 --- a/examples/dat/hsm/sasp.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 2020-2022 NXP -# -# SPDX-License-Identifier: BSD-3-Clause - -"""Customer-specific Signature Provider.""" - -import base64 - -import requests # type: ignore - -from spsdk.crypto import SignatureProvider - - -class SuperAwesomeSP(SignatureProvider): - """Signature Provider based on a remote signing service.""" - - # identifier of this signature provider; used in yaml configuration file - sp_type = "sasp" - - def __init__(self, key_number: int) -> None: - """Initialize the Super Awesome SignatureProvider. - - :param key_number: index of the key to use (rot_id from yaml config) - """ - self.url = f"http://127.0.0.1:5000/signer/{key_number}" - - def info(self) -> str: - """Return basic info about the Signature provider.""" - msg = "Super Awesome Signature Provider\n" - msg += f"Remote URL: {self.url}\n" - return msg - - def sign(self, data: bytes) -> bytes: - """Perform the signing. - - :param data: Data to sign - :return: Signature - """ - params = {"data": base64.b64encode(data)} - response = requests.get(self.url, params=params) - signature = response.json()["signature"] - data = base64.b64decode(signature) - return data.zfill(256) - - @property - def signature_length(self) -> int: - """Return length of the signature.""" - return 256 diff --git a/examples/dat/p0_cert0_2048.pub b/examples/dat/p0_cert0_2048.pub deleted file mode 100644 index 39d52639..00000000 --- a/examples/dat/p0_cert0_2048.pub +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEApLuDyNSAV8iId3Jb5KE1mKloQ/d2hwxpG6Ek3Kp5EEHCjfQ4PqBY -mSfIeDSJZ4uUfn9mshE3oszq6YfwONWU9mXIPmvrbO9gaLQJU8DZ4AROERiZAdoZ -ND7aQCowAH/G165k010A8+AYNM7XjT43ofxbsKrOgZq0I0FHJzVR3fqU4ePRL+25 -ebyMxXbCaq6LZOnOGkJxarbDtGbaOQhu8BGp7kWKzjIQXMNF6qnc6Tvtb214JN/q -O4qAYDBNT533tXHupAYeZf38r/CnTrbQHZaqsz64w2QK0K/YgFu2c0qHEmT8bJgt -ldrWx162x9blU/x/PMn+lBR2EANl9ex5fwIDAQAB ------END RSA PUBLIC KEY----- diff --git a/examples/dat/p1_cert0_2048.pub b/examples/dat/p1_cert0_2048.pub deleted file mode 100644 index f509bc33..00000000 --- a/examples/dat/p1_cert0_2048.pub +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEAvraadhXvAoa41bJQp07yGRz4pzy3BIg/kVO/Ypp2gQvw7uSPnLxz -6UhHUZjB3uaer7XTQy2PKMJ+yZ/Qj9pPcnJhz8w5WVVKjzGN3c6OH8nDOvJkt+Qe -AXLqkxtqa/rhZTxN0d1KwFjOJONqrhrBSzSsEKPbpik7jDJwAcUbwY+e+eBukEA+ -KxH4s9h0SUiRbeUDgRVCXkA4ISVQjtL79HW4bghl0FwQ+JE6z4Hk1Xo2B9RA7lbY -E48a3I7HyGhV6vYrIA8RrPCT/vdR6VE+8N79YjoVLo3aLZNBywKMlRF9gIMOBDVT -lR8f77+tj16yJC1ItfvruijkbWswsKYqSQIDAQAB ------END RSA PUBLIC KEY----- diff --git a/examples/data/cfpa_lpc55s6x_test.yaml b/examples/data/cfpa_lpc55s6x_test.yaml new file mode 100644 index 00000000..c88ffc88 --- /dev/null +++ b/examples/data/cfpa_lpc55s6x_test.yaml @@ -0,0 +1,37 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# NXP lpc55s6x PFR CFPA configuration +description: # The CFPA configuration description. + device: lpc55s6x # The NXP device name. + revision: 1b # The NXP device revision. + type: CFPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.10.0 # The SPSDK tool version. +settings: # The CFPA registers configuration. + VERSION: # Version + value: "0x0000_0002" # The value width: 32b + ROTKH_REVOKE: # Root of Trust Key Hash Revoke + bitfields: # The register bitfields + RoTK0_EN: 0b01 # Width: 2b, Description: RoT Key 0 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK1_EN: 0b01 # Width: 2b, Description: RoT Key 1 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK2_EN: 0b01 # Width: 2b, Description: RoT Key 2 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK3_EN: 0b01 # Width: 2b, Description: RoT Key 3 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + CMPA_PROG_IN_PROGRESS: # 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. + value: "0x5cc5_5aa5" # The value width: 32b diff --git a/examples/data/cfpa_test.json b/examples/data/cfpa_test.json deleted file mode 100644 index 00ba82aa..00000000 --- a/examples/data/cfpa_test.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "VERSION": "0x0000_0002", - "ROTKH_REVOKE": { - "RoTK0_EN": "0b01", - "RoTK1_EN": "0b01", - "RoTK2_EN": "0b01", - "RoTK3_EN": "0b01" - }, - "CMPA_PROG_IN_PROGRESS": "0x5cc5_5aa5" -} \ No newline at end of file diff --git a/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_boot.ipynb b/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_boot.ipynb index 1e43ba07..8f5a33b3 100644 --- a/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_boot.ipynb +++ b/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_boot.ipynb @@ -16,9 +16,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created `%!` as an alias for `%execute`.\n", + "nxpimage -v utils binary-image convert -i lpcxpresso55s69_led_blinky.axf -f BIN workspace/lpcxpresso55s69_led_blinky.bin \n", + "Success. (Converted file: workspace\\lpcxpresso55s69_led_blinky.bin created.)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.images:Elf file support is experimental. Take that with care. (1494ms since start, images.py:531)\n", + "INFO:spsdk.apps.nxpimage:\n", + "+==0x0000_0000= lpcxpresso55s69_led_blinky.axf ==+\n", + "| Size: 8.8 kB |\n", + "|The image loaded from: c:/repos/spsdk_master/exa|\n", + "|mples/jupyter_examples/lpc55sxx_secure_boot/lpcx|\n", + "| presso55s69_led_blinky.axf . |\n", + "|+==0x0000_0000= Segment 0 =====================+|\n", + "|| Size: 8.8 kB ||\n", + "|+==0x0000_2273=================================+|\n", + "+==0x0000_2273===================================+\n", + "\n" + ] + } + ], "source": [ "%alias execute echo %l && %l\n", "%alias_magic ! execute\n", @@ -37,7 +65,6 @@ "# convert the elf file to bin using nxpimage\n", "%! nxpimage $VERBOSITY utils binary-image convert -i $ELF_PATH -f BIN $BINARY_FILE\n", "\n", - "assert _exit_code == 0\n", "assert os.path.exists(BINARY_FILE)" ] }, @@ -69,9 +96,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpimage -v mbi get-templates -f lpc55s6x workspace/templates \n", + "Creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_plain.yaml template file.\n", + "Creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_crc.yaml template file.\n", + "Creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_signed.yaml template file.\n", + "Creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_ram_crc.yaml template file.\n", + "Creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_ram_signed.yaml template file.\n" + ] + } + ], "source": [ "# generate template for mbi\n", "TEMPLATES_PATH = WORKSPACE + \"templates\"\n", @@ -80,8 +120,7 @@ "\n", "%! nxpimage $VERBOSITY mbi get-templates -f $FAMILY $TEMPLATES_PATH\n", "# just for verification that the template was generated\n", - "assert _exit_code == 0\n", - "assert os.path.exists(os.path.join(TEMPLATES_PATH, \"lpc55s6x_int_xip_signed.yml\"))" + "assert os.path.exists(os.path.join(TEMPLATES_PATH, \"lpc55s6x_int_xip_signed.yaml\"))" ] }, { @@ -104,7 +143,7 @@ "# == Trust Zone Settings == \n", "# ----------------------------------------------------------------------------------------------------\n", "enableTrustZone: false # [Optional], TrustZone enable option, If not specified, the Trust zone is disabled.\n", - "trustZonePresetFile: my_tz_custom.yml # [Optional], TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used.\n", + "trustZonePresetFile: my_tz_custom.yaml # [Optional], TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used.\n", "# ----------------------------------------------------------------------------------------------------\n", "# == Certificate V2 Settings == \n", "# ----------------------------------------------------------------------------------------------------\n", @@ -176,16 +215,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v key generate -k rsa2048 workspace/rsa2048_key.pem --force \n", + "The key pair has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\rsa2048_key.pub, C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\rsa2048_key.pem\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpkeygen:Generating RSA private key...\n", + "INFO:spsdk.apps.nxpkeygen:Generating RSA corresponding public key...\n", + "INFO:spsdk.apps.nxpkeygen:Saving RSA key pair...\n" + ] + } + ], "source": [ "# generate RSA 2048 public and private key\n", "PRIVATE_KEY_PATH = WORKSPACE + \"rsa2048_key.pem\"\n", "PUBLIC_KEY_PATH = WORKSPACE + \"rsa2048_key.pub\"\n", "\n", "%! nxpcrypto $VERBOSITY key generate -k rsa2048 $PRIVATE_KEY_PATH --force\n", - "assert _exit_code == 0\n", "\n", "# verify that keys were generated\n", "assert os.path.exists(PRIVATE_KEY_PATH)\n", @@ -203,19 +259,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert get-template workspace/root_cert_template.yml --force \n", + "The configuration template file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\root_cert_template.yml\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert get-template workspace/chain_cert_template.yml --force \n", + "The configuration template file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\chain_cert_template.yml\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n" + ] + } + ], "source": [ "# obtain a template for root cert\n", "ROOT_CERT_CONFIG_PATH = WORKSPACE + \"root_cert_template.yml\"\n", "%! nxpcrypto $VERBOSITY cert get-template $ROOT_CERT_CONFIG_PATH --force\n", - "assert _exit_code == 0\n", "\n", "# obtain a template for root cert\n", "CHAIN_CERT_CONFIG_PATH = WORKSPACE + \"chain_cert_template.yml\"\n", - "%! nxpcrypto $VERBOSITY cert get-template $CHAIN_CERT_CONFIG_PATH --force\n", - "assert _exit_code == 0" + "%! nxpcrypto $VERBOSITY cert get-template $CHAIN_CERT_CONFIG_PATH --force\n" ] }, { @@ -282,9 +367,83 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Root Certificate config:\n", + "{ 'duration': 3650,\n", + " 'extensions': {'BASIC_CONSTRAINTS': {'ca': True, 'path_length': 0}},\n", + " 'issuer': { 'COMMON_NAME': 'NXP',\n", + " 'COUNTRY_NAME': 'CZ',\n", + " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", + " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", + " 'serial_number': 12346578,\n", + " 'subject': { 'COMMON_NAME': 'NXP',\n", + " 'COUNTRY_NAME': 'CZ',\n", + " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", + " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'subject_public_key': 'workspace/rsa2048_key.pub'}\n", + "Chain certificate config:\n", + "{ 'duration': 3650,\n", + " 'extensions': {'BASIC_CONSTRAINTS': {'ca': False, 'path_length': 0}},\n", + " 'issuer': { 'COMMON_NAME': 'NXP',\n", + " 'COUNTRY_NAME': 'CZ',\n", + " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", + " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", + " 'serial_number': 12346578,\n", + " 'subject': { 'COMMON_NAME': 'NXP - SPSDK',\n", + " 'COUNTRY_NAME': 'CZ',\n", + " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", + " 'POSTAL_CODE': '756 61',\n", + " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'subject_public_key': 'workspace/rsa2048_key.pub'}\n", + "nxpcrypto -v cert generate -c workspace/root_cert_template.yml -e DER -o workspace/ROT1_sha256_2048_65537_v3_ca_crt.der --force \n", + "The certificate file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\ROT1_sha256_2048_65537_v3_ca_crt.der\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Generating Certificate...\n", + "INFO:spsdk.apps.nxpcertgen:Loading configuration from yml file...\n", + "INFO:spsdk.apps.nxpcertgen:Saving the generated certificate to the specified path...\n", + "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert generate -c workspace/chain_cert_template.yml -e DER -o workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem --force \n", + "The certificate file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\IMG1_1_sha256_2048_65537_v3_usr_key.pem\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Generating Certificate...\n", + "INFO:spsdk.apps.nxpcertgen:Loading configuration from yml file...\n", + "INFO:spsdk.apps.nxpcertgen:Saving the generated certificate to the specified path...\n", + "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n" + ] + } + ], "source": [ "ROOT_0_CERT_PATH = WORKSPACE + \"ROT1_sha256_2048_65537_v3_ca_crt.der\"\n", "CHAIN_CERT_0 = WORKSPACE + \"IMG1_1_sha256_2048_65537_v3_usr_key.pem\"\n", @@ -330,10 +489,8 @@ " \n", "# Generate root certificate\n", "%! nxpcrypto $VERBOSITY cert generate -c $ROOT_CERT_CONFIG_PATH -e DER -o $ROOT_0_CERT_PATH --force\n", - "assert _exit_code == 0\n", "# Generate chain certificate\n", "%! nxpcrypto $VERBOSITY cert generate -c $CHAIN_CERT_CONFIG_PATH -e DER -o $CHAIN_CERT_0 --force\n", - "assert _exit_code == 0\n", "\n", "# verify that certificates were generated\n", "assert os.path.exists(ROOT_0_CERT_PATH)\n", @@ -351,9 +508,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpimage -v mbi export workspace/mbi_config_lpc55s6x.yml \n", + "Success. (Master Boot Image: c:/repos/spsdk_master/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/lpc55s6x_mbi.bin created.)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.image.mbi_mixin:RKTH: 59e0079da0c53fefbd4a28f2cc7066b8a3921ad9c3e4661dd4cf7ebf6ed13f02\n" + ] + } + ], "source": [ "MBI_BIN_NAME = \"lpc55s6x_mbi.bin\"\n", "MBI_CONFIG_PATH = WORKSPACE + \"mbi_config_lpc55s6x.yml\"\n", @@ -388,7 +561,6 @@ "\n", "# export Master Boot Image\n", "%! nxpimage $VERBOSITY mbi export $MBI_CONFIG_PATH\n", - "assert _exit_code == 0\n", "\n", "BIN_OUTPUT_PATH = WORKSPACE + MBI_BIN_NAME\n", "assert os.path.exists(BIN_OUTPUT_PATH)\n" @@ -422,9 +594,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pfr get-template -t cmpa -d lpc55s6x -o workspace/cmpa_lpc55s6x.yml \n", + "PFR cmpa configuration template has been created.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cmpa_lpc55s6x.yml\n", + "pfr get-template -t cfpa -d lpc55s6x -o workspace/cfpa_lpc55s6x.yml \n", + "PFR cfpa configuration template has been created.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa_lpc55s6x.yml\n" + ] + } + ], "source": [ "\n", "CMPA_TEMPLATE_PATH = WORKSPACE + \"cmpa_lpc55s6x.yml\"\n", @@ -432,11 +617,9 @@ "\n", "# get CMPA template\n", "%! pfr get-template -t cmpa -d $FAMILY -o $CMPA_TEMPLATE_PATH\n", - "assert _exit_code == 0\n", "\n", "# get CFPA template\n", - "%! pfr get-template -t cfpa -d $FAMILY -o $CFPA_TEMPLATE_PATH\n", - "assert _exit_code == 0" + "%! pfr get-template -t cfpa -d $FAMILY -o $CFPA_TEMPLATE_PATH" ] }, { @@ -448,9 +631,149 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CMPA config:\n", + "{ 'description': { 'device': 'lpc55s6x',\n", + " 'revision': '1b',\n", + " 'type': 'CMPA',\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'BOOT_CFG': { 'bitfields': { 'BOOT_FAILURE_PIN': '0x00',\n", + " 'BOOT_SPEED': 'SYSTEM_SPEED_CODE',\n", + " 'DEFAULT_ISP_MODE': 'AUTO_ISP',\n", + " 'USB_SPEED': 'USB_SPEED_0'}},\n", + " 'CUSTOMER_DEFINED0': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED1': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED10': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED11': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED12': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED13': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED14': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED15': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED16': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED17': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED18': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED19': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED20': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED21': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED22': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED23': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED24': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED25': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED26': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED27': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED28': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED29': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED30': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED31': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED32': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED33': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED34': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED35': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED36': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED37': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED38': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED39': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED40': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED41': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED42': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED43': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED44': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED45': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED46': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED47': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED48': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED49': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED50': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED51': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED52': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED53': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED54': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED55': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", + " 'DCFG_CC_SOCU_DFLT': { 'bitfields': { 'CPU1_DBGEN': 'DISABLED',\n", + " 'CPU1_NIDEN': 'DISABLED',\n", + " 'DBGEN': 'DISABLED',\n", + " 'FA_ME_CMD_EN': 'DISABLED',\n", + " 'ISP_CMD_EN': 'DISABLED',\n", + " 'NIDEN': 'DISABLED',\n", + " 'SPIDEN': 'DISABLED',\n", + " 'SPNIDEN': 'DISABLED',\n", + " 'TAPEN': 'DISABLED'}},\n", + " 'DCFG_CC_SOCU_PIN': { 'bitfields': { 'CPU1_DBGEN': 'USE_DAP',\n", + " 'CPU1_NIDEN': 'USE_DAP',\n", + " 'DBGEN': 'USE_DAP',\n", + " 'FA_ME_CMD_EN': 'USE_DAP',\n", + " 'ISP_CMD_EN': 'USE_DAP',\n", + " 'NIDEN': 'USE_DAP',\n", + " 'SPIDEN': 'USE_DAP',\n", + " 'SPNIDEN': 'USE_DAP',\n", + " 'TAPEN': 'USE_DAP',\n", + " 'UUID_CHECK': 'DISABLED'}},\n", + " 'PRINCE_BASE_ADDR': { 'bitfields': { 'ADDR0_PRG': '0x0',\n", + " 'ADDR1_PRG': '0x0',\n", + " 'ADDR2_PRG': '0x0',\n", + " 'LOCK_REG0': 'UNLOCK',\n", + " 'LOCK_REG1': 'UNLOCK',\n", + " 'REG0_ERASE_CHECK_EN': 'DISABLE',\n", + " 'REG1_ERASE_CHECK_EN': 'DISABLE',\n", + " 'REG2_ERASE_CHECK_EN': 'DISABLE'}},\n", + " 'PRINCE_SR_0': {'value': '0x00000000'},\n", + " 'PRINCE_SR_1': {'value': '0x00000000'},\n", + " 'PRINCE_SR_2': {'value': '0x00000000'},\n", + " 'ROTKH': { 'value': '0000000000000000000000000000000000000000000000000000000000000000'},\n", + " 'SDIO_CFG': {'value': '0x00000000'},\n", + " 'SECURE_BOOT_CFG': { 'bitfields': { 'BLOCK_ENROLL': 'ALLOW',\n", + " 'BLOCK_SET_KEY': 'ALLOW',\n", + " 'DICE_CUST_CFG': 'NOT_INCLUDE',\n", + " 'DICE_INC_NXP_CFG': 'NOT_INCLUDE',\n", + " 'DICE_INC_SEC_EPOCH': '0x0',\n", + " 'RSA4K': 'RSA2048',\n", + " 'SEC_BOOT_EN': 'SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0',\n", + " 'SKIP_DICE': 'SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0',\n", + " 'TZM_IMAGE_TYPE': 'HEADER'}},\n", + " 'SPI_FLASH_CFG': { 'bitfields': { 'SPI_RECOVERY_BOOT_EN': '0x0'}},\n", + " 'USB_ID': { 'bitfields': { 'USB_PRODUCT_ID': '0x0000',\n", + " 'USB_VENDOR_ID': '0x0000'}},\n", + " 'VENDOR_USAGE': {'bitfields': {'VENDOR_USAGE': '0x0000'}},\n", + " 'XTAL_16MHZ_CAPABANK_TRIM': { 'bitfields': { 'PCB_XIN_PARA_CAP_PF_X100': '0x00',\n", + " 'PCB_XOUT_PARA_CAP_PF_X100': '0x00',\n", + " 'TRIM_VALID': 'NOT_TRIM',\n", + " 'XTAL_LOAD_CAP_IEC_PF_X100': '0x00'}},\n", + " 'XTAL_32KHZ_CAPABANK_TRIM': { 'bitfields': { 'PCB_XIN_PARA_CAP_PF_X100': '0x00',\n", + " 'PCB_XOUT_PARA_CAP_PF_X100': '0x00',\n", + " 'TRIM_VALID': 'NOT_TRIM',\n", + " 'XTAL_LOAD_CAP_IEC_PF_X100': '0x00'}}}}\n", + "pfr -v generate-binary -c workspace/cmpa_lpc55s6x.yml -o workspace/cmpa.bin -e workspace/mbi_config_lpc55s6x.yml \n", + "Success. (PFR binary has been generated.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cmpa.bin\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.registers:Bitfield SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0 not found, trying backward compatibility mode with ENABLE_0 (1320ms since start, registers.py:1201)\n", + "WARNING:spsdk.utils.registers:Bitfield SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 not found, trying backward compatibility mode with DISABLE_0 (1320ms since start, registers.py:1201)\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.utils.crypto.rkht:ROTKH: 59e0079da0c53fefbd4a28f2cc7066b8a3921ad9c3e4661dd4cf7ebf6ed13f02\n" + ] + } + ], "source": [ "assert os.path.exists(CMPA_TEMPLATE_PATH)\n", " \n", @@ -472,7 +795,6 @@ "\n", "# Generate CMPA binary\n", "%! pfr $VERBOSITY generate-binary -c $CMPA_TEMPLATE_PATH -o $CMPA_BIN -e $MBI_CONFIG_PATH\n", - "assert _exit_code == 0\n", "\n", "assert os.path.exists(CMPA_BIN)" ] @@ -498,9 +820,59 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpdevscan \n", + "-------- Connected NXP USB Devices --------\n", + "\n", + "LPC-LINK2 CMSIS-DAP V5.224 - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_00\\7&36E95AFB&0&0000\n", + "Path Hash: 77e5c391\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPC-LINK2 DATA PORT - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_04\\7&25C3B40A&0&0000\n", + "Path Hash: c297936f\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPCSIO - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "-------- Connected NXP UART Devices --------\n", + "\n", + "Port: COM25\n", + "Type: mboot device\n", + "\n", + "-------- Connected NXP SIO Devices --------\n", + "\n", + "LIBUSBSIO - NXP Semiconductors, LPCSIO\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Serial number: NRA2AQHR\n", + "Interface number: 3\n", + "Release number: 256\n", + "\n" + ] + } + ], "source": [ "# check if the device is connected and detected by PC\n", "%! nxpdevscan" @@ -508,20 +880,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "blhost -p com25 get-property current-version \n", + "Response status = 0 (0x0) Success.\n", + "Response word 1 = 1258487808 (0x4b030000)\n", + "Current Version = K3.0.0\n" + ] + } + ], "source": [ "USB_CONNECTION = \"-u lpc55\" \n", "# choose com port or /dev\n", - "UART_CONNECTION = \"-p com6\"\n", + "UART_CONNECTION = \"-p com25\"\n", "\n", "# comment if you want to use UART\n", - "CONNECTION = USB_CONNECTION\n", + "CONNECTION = UART_CONNECTION\n", "# CONNECTION = UART_CONNECTION\n", "\n", - "%! blhost $CONNECTION get-property current-version\n", - "assert _exit_code == 0" + "%! blhost $CONNECTION get-property current-version\n" ] }, { @@ -535,21 +917,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "blhost -p com25 set-property 29 1 \n", + "Response status = 0 (0x0) Success.\n", + "blhost -p com25 write-memory 0x9E600 zero_1536.bin \n", + "Writing memory\n", + "Response status = 0 (0x0) Success.\n", + "Response word 1 = 1536 (0x600)\n", + "blhost -p com25 set-property 29 0 \n", + "Response status = 0 (0x0) Success.\n" + ] + } + ], "source": [ "# first you need to set property 29 - PFR key store update option\n", "%! blhost $CONNECTION set-property 29 1\n", - "assert _exit_code == 0\n", "\n", "# now write the file containing zero bytes to the location of key store\n", "%! blhost $CONNECTION write-memory 0x9E600 zero_1536.bin\n", - "assert _exit_code == 0\n", "\n", "# set the property 29 back to 0\n", - "%! blhost $CONNECTION set-property 29 0\n", - "assert _exit_code == 0" + "%! blhost $CONNECTION set-property 29 0\n" ] }, { @@ -567,14 +961,188 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pfr read -d lpc55s6x -p com25 -t cfpa -o workspace/cfpa.bin -y workspace/cfpa_parsed.yaml --show-diff \n", + "CFPA page address on lpc55s6x is 0x9de00\n", + "CFPA data stored to C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa.bin\n", + "Parsed config stored to C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa_parsed.yaml\n", + "Parsed CFPA from the processor\n", + "{ 'description': { 'device': 'lpc55s6x',\n", + " 'revision': '1b',\n", + " 'type': 'CFPA',\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'CMPA_PROG_IN_PROGRESS': {'value': '0x5CC55AA5'},\n", + " 'ROTKH_REVOKE': {'bitfields': {'RoTK0_EN': 'ENABLED'}},\n", + " 'VERSION': {'value': '0x00000001'}}}\n", + "CFPA config:\n", + "{ 'description': { 'device': 'lpc55s6x',\n", + " 'revision': '1b',\n", + " 'type': 'CFPA',\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'CMPA_PROG_IN_PROGRESS': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED0': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED1': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED10': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED11': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED12': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED13': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED14': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED15': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED16': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED17': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED18': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED19': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED20': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED21': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED22': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED23': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED24': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED25': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED26': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED27': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED28': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED29': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED30': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED31': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED32': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED33': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED34': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED35': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED36': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED37': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED38': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED39': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED40': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED41': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED42': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED43': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED44': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED45': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED46': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED47': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED48': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED49': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED50': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED51': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED52': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED53': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED54': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED55': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", + " 'DCFG_CC_SOCU_NS_DFLT': { 'bitfields': { 'CPU1_DBGEN': 'DISABLED',\n", + " 'CPU1_NIDEN': 'DISABLED',\n", + " 'DBGEN': 'DISABLED',\n", + " 'FA_ME_CMD_EN': 'DISABLED',\n", + " 'ISP_CMD_EN': 'DISABLED',\n", + " 'NIDEN': 'DISABLED',\n", + " 'SPIDEN': 'DISABLED',\n", + " 'SPNIDEN': 'DISABLED',\n", + " 'TAPEN': 'DISABLED'}},\n", + " 'DCFG_CC_SOCU_NS_PIN': { 'bitfields': { 'CPU1_DBGEN': 'USE_DAP',\n", + " 'CPU1_NIDEN': 'USE_DAP',\n", + " 'DBGEN': 'USE_DAP',\n", + " 'FA_ME_CMD_EN': 'USE_DAP',\n", + " 'ISP_CMD_EN': 'USE_DAP',\n", + " 'NIDEN': 'USE_DAP',\n", + " 'SPIDEN': 'USE_DAP',\n", + " 'SPNIDEN': 'USE_DAP',\n", + " 'TAPEN': 'USE_DAP',\n", + " 'UUID_CHECK': 'DISABLED'}},\n", + " 'ENABLE_FA_MODE': {'value': '0x00000000'},\n", + " 'HEADER': {'value': '0x00000000'},\n", + " 'IMAGE_KEY_REVOKE': {'value': '0x00000000'},\n", + " 'NS_FW_Version': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'PRINCE_REGION1_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'PRINCE_REGION2_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'ROTKH_REVOKE': { 'bitfields': { 'RoTK0_EN': 'ROTKH_REVOKE_RoTK0_EN_ENABLED',\n", + " 'RoTK1_EN': 'INVALID',\n", + " 'RoTK2_EN': 'INVALID',\n", + " 'RoTK3_EN': 'INVALID'}},\n", + " 'S_FW_Version': {'value': '0x00000000'},\n", + " 'VENDOR_USAGE': { 'bitfields': { 'DBG_VENDOR_USAGE': '0x0000'}},\n", + " 'VERSION': {'value': '0x2'}}}\n", + "pfr -v generate-binary -c workspace/cfpa_lpc55s6x.yml -o workspace/cfpa.bin \n", + "Success. (PFR binary has been generated.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa.bin\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.registers:Bitfield ROTKH_REVOKE_RoTK0_EN_ENABLED not found, trying backward compatibility mode with ENABLED (1668ms since start, registers.py:1201)\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n" + ] + } + ], "source": [ "CFPA_BIN = WORKSPACE + \"cfpa.bin\"\n", "CFPA_PARSED = WORKSPACE + \"cfpa_parsed.yaml\"\n", "\n", - "# First, read the current CFPA page on the procesor and parse it to YAML, show only differences\n", + "# First, read the current CFPA page on the processor and parse it to YAML\n", "%! pfr read -d $FAMILY $CONNECTION -t cfpa -o $CFPA_BIN -y $CFPA_PARSED --show-diff\n", "\n", "assert os.path.exists(CFPA_TEMPLATE_PATH)\n", @@ -606,7 +1174,6 @@ "\n", "# Generate CFPA binary\n", "%! pfr $VERBOSITY generate-binary -c $CFPA_TEMPLATE_PATH -o $CFPA_BIN\n", - "assert _exit_code == 0\n", "\n", "assert os.path.exists(CFPA_BIN)" ] @@ -621,19 +1188,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pfr write -p com25 -t cfpa -d lpc55s6x -b workspace/cfpa.bin \n", + "CFPA page address on lpc55s6x is 0x9de00\n", + "CFPA data written to device.\n", + "pfr write -p com25 -t cmpa -d lpc55s6x -b workspace/cmpa.bin \n", + "CMPA page address on lpc55s6x is 0x9e400\n", + "CMPA data written to device.\n" + ] + } + ], "source": [ "# write CFPA\n", "%! pfr write $CONNECTION -t cfpa -d $FAMILY -b $CFPA_BIN\n", - "assert _exit_code == 0\n", "# this is the same as \n", "#%! blhost $CONNECTION write-memory 0x0009DE00 $CFPA_BIN\"\n", "\n", "# write CMPA\n", - "%! pfr write $CONNECTION -t cmpa -d $FAMILY -b $CMPA_BIN\n", - "assert _exit_code == 0" + "%! pfr write $CONNECTION -t cmpa -d $FAMILY -b $CMPA_BIN\n" ] }, { @@ -647,17 +1225,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "blhost -p com25 flash-erase-region 0 0x10000 \n", + "Response status = 0 (0x0) Success.\n", + "blhost -p com25 write-memory 0 workspace/lpc55s6x_mbi.bin \n", + "Writing memory\n", + "Response status = 0 (0x0) Success.\n", + "Response word 1 = 11064 (0x2b38)\n" + ] + } + ], "source": [ "# Erase flash first\n", "%! blhost $CONNECTION flash-erase-region 0 0x10000\n", - "assert _exit_code == 0\n", "\n", "# write MBI\n", - "%! blhost $CONNECTION write-memory 0 $BIN_OUTPUT_PATH\n", - "assert _exit_code == 0" + "%! blhost $CONNECTION write-memory 0 $BIN_OUTPUT_PATH" ] } ], @@ -680,7 +1269,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.9.0" } }, "nbformat": 4, diff --git a/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_fw_update.ipynb b/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_fw_update.ipynb index 2f093e42..d91d609d 100644 --- a/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_fw_update.ipynb +++ b/examples/jupyter_examples/lpc55sxx_secure_boot/lpc55sxx_secure_fw_update.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -22,165 +22,190 @@ "output_type": "stream", "text": [ "Created `%!` as an alias for `%execute`.\n", - "nxpimage -v utils binary-image convert -i lpcxpresso55s69_led_blinky.axf -f BIN workspace/lpcxpresso55s69_led_blinky.bin\n", - "WARNING:spsdk.utils.images:Elf file support is experimental. Take that with care.\n", + "nxpimage -v utils binary-image convert -i lpcxpresso55s69_led_blinky.axf -f BIN workspace/lpcxpresso55s69_led_blinky.bin \n", + "Success. (Converted file: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\lpcxpresso55s69_led_blinky.bin created.)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.images:Elf file support is experimental. Take that with care. (1987ms since start, images.py:531)\n", "INFO:spsdk.apps.nxpimage:\n", - "\u001b[90m+--0x0000_0000--lpcxpresso55s69_led_blinky.axf--+\n", - "\u001b[90m| Size: 8.8 kB |\n", - "\u001b[90m|The image loaded from: /Users/macbook-m1/spsdk_|\n", - "\u001b[90m|test/spsdk/examples/jupyter_examples/lpc55sxx_s|\n", - "\u001b[90m| ecure_boot/lpcxpresso55s69_led_blinky.axf . |\n", - "\u001b[90m|\u001b[34m+--0x0000_0000--Segment 0---------------------+\u001b[90m|\n", - "\u001b[90m|\u001b[34m| Size: 8.8 kB |\u001b[90m|\n", - "\u001b[90m|\u001b[34m+--0x0000_2273--------------------------------+\u001b[90m|\n", - "\u001b[90m+--0x0000_2273----------------------------------+\n", - "\u001b[39m\n", - "Success. (Converted file: workspace/lpcxpresso55s69_led_blinky.bin created.)\n", - "nxpimage -v mbi get-templates -f lpc55s6x workspace/templates\n", - "Skip creating workspace/templates/lpc55s6x_int_xip_plain.yml, this file already exists.\n", - "Skip creating workspace/templates/lpc55s6x_int_xip_crc.yml, this file already exists.\n", - "Skip creating workspace/templates/lpc55s6x_int_xip_signed.yml, this file already exists.\n", - "Skip creating workspace/templates/lpc55s6x_ram_crc.yml, this file already exists.\n", - "Skip creating workspace/templates/lpc55s6x_ram_signed.yml, this file already exists.\n", - "nxpcrypto -v key generate -k rsa2048 workspace/rsa2048_key.pem --force\n", + "+==0x0000_0000= lpcxpresso55s69_led_blinky.axf ==+\n", + "| Size: 8.8 kB |\n", + "|The image loaded from: c:/repos/spsdk_master/exa|\n", + "|mples/jupyter_examples/lpc55sxx_secure_boot/lpcx|\n", + "| presso55s69_led_blinky.axf . |\n", + "|+==0x0000_0000= Segment 0 =====================+|\n", + "|| Size: 8.8 kB ||\n", + "|+==0x0000_2273=================================+|\n", + "+==0x0000_2273===================================+\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpimage -v mbi get-templates -f lpc55s6x workspace/templates \n", + "Skip creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_plain.yaml, this file already exists.\n", + "Skip creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_crc.yaml, this file already exists.\n", + "Skip creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_int_xip_signed.yaml, this file already exists.\n", + "Skip creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_ram_crc.yaml, this file already exists.\n", + "Skip creating c:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\templates\\lpc55s6x_ram_signed.yaml, this file already exists.\n", + "nxpcrypto -v key generate -k rsa2048 workspace/rsa2048_key.pem --force \n", + "The key pair has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\rsa2048_key.pub, C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\rsa2048_key.pem\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ "INFO:spsdk.apps.nxpkeygen:Generating RSA private key...\n", "INFO:spsdk.apps.nxpkeygen:Generating RSA corresponding public key...\n", - "INFO:spsdk.apps.nxpkeygen:Saving RSA key pair...\n", - "nxpcrypto -v cert get-template workspace/root_cert_template.yml --force\n", - "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n", - "The configuration template file has been created: /Users/macbook-m1/spsdk_test/spsdk/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/root_cert_template.yml\n", - "nxpcrypto -v cert get-template workspace/chain_cert_template.yml --force\n", - "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n", - "The configuration template file has been created: /Users/macbook-m1/spsdk_test/spsdk/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/chain_cert_template.yml\n", + "INFO:spsdk.apps.nxpkeygen:Saving RSA key pair...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert get-template workspace/root_cert_template.yml --force \n", + "The configuration template file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\root_cert_template.yml\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert get-template workspace/chain_cert_template.yml --force \n", + "The configuration template file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\chain_cert_template.yml\n", "Root Certificate config:\n", - "{ 'issuer': { 'COMMON_NAME': 'NXP',\n", + "{ 'duration': 3650,\n", + " 'extensions': {'BASIC_CONSTRAINTS': {'ca': True, 'path_length': 0}},\n", + " 'issuer': { 'COMMON_NAME': 'NXP',\n", " 'COUNTRY_NAME': 'CZ',\n", " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", - " 'STREET_ADDRESS': '1.maje 1009',\n", - " 'ORGANIZATION_NAME': 'SPSDK Team'},\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", + " 'serial_number': 12346578,\n", " 'subject': { 'COMMON_NAME': 'NXP',\n", " 'COUNTRY_NAME': 'CZ',\n", " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", - " 'STREET_ADDRESS': '1.maje 1009',\n", - " 'ORGANIZATION_NAME': 'SPSDK Team'},\n", - " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", - " 'subject_public_key': 'workspace/rsa2048_key.pub',\n", - " 'serial_number': 12346578,\n", - " 'duration': 3650,\n", - " 'extensions': {'BASIC_CONSTRAINTS': {'ca': True, 'path_length': 0}}}\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'subject_public_key': 'workspace/rsa2048_key.pub'}\n", "Chain certificate config:\n", - "{ 'issuer': { 'COMMON_NAME': 'NXP',\n", + "{ 'duration': 3650,\n", + " 'extensions': {'BASIC_CONSTRAINTS': {'ca': False, 'path_length': 0}},\n", + " 'issuer': { 'COMMON_NAME': 'NXP',\n", " 'COUNTRY_NAME': 'CZ',\n", " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", + " 'ORGANIZATION_NAME': 'SPSDK Team',\n", " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", - " 'STREET_ADDRESS': '1.maje 1009',\n", - " 'ORGANIZATION_NAME': 'SPSDK Team'},\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", + " 'serial_number': 12346578,\n", " 'subject': { 'COMMON_NAME': 'NXP - SPSDK',\n", " 'COUNTRY_NAME': 'CZ',\n", " 'LOCALITY_NAME': 'Roznov pod Radhostem',\n", - " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", - " 'STREET_ADDRESS': '1.maje 1009',\n", " 'ORGANIZATION_NAME': 'SPSDK Team',\n", - " 'POSTAL_CODE': '756 61'},\n", - " 'issuer_private_key': 'workspace/rsa2048_key.pem',\n", - " 'subject_public_key': 'workspace/rsa2048_key.pub',\n", - " 'serial_number': 12346578,\n", - " 'duration': 3650,\n", - " 'extensions': {'BASIC_CONSTRAINTS': {'ca': False, 'path_length': 0}}}\n", - "nxpcrypto -v cert generate -c workspace/root_cert_template.yml -e DER -o workspace/ROT1_sha256_2048_65537_v3_ca_crt.der --force\n", + " 'POSTAL_CODE': '756 61',\n", + " 'STATE_OR_PROVINCE_NAME': 'Morava',\n", + " 'STREET_ADDRESS': '1.maje 1009'},\n", + " 'subject_public_key': 'workspace/rsa2048_key.pub'}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.apps.nxpcertgen:Creating Certificate template...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert generate -c workspace/root_cert_template.yml -e DER -o workspace/ROT1_sha256_2048_65537_v3_ca_crt.der --force \n", + "The certificate file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\ROT1_sha256_2048_65537_v3_ca_crt.der\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ "INFO:spsdk.apps.nxpcertgen:Generating Certificate...\n", "INFO:spsdk.apps.nxpcertgen:Loading configuration from yml file...\n", "INFO:spsdk.apps.nxpcertgen:Saving the generated certificate to the specified path...\n", - "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n", - "The certificate file has been created: /Users/macbook-m1/spsdk_test/spsdk/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/ROT1_sha256_2048_65537_v3_ca_crt.der\n", - "nxpcrypto -v cert generate -c workspace/chain_cert_template.yml -e DER -o workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem --force\n", + "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpcrypto -v cert generate -c workspace/chain_cert_template.yml -e DER -o workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem --force \n", + "The certificate file has been created: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\IMG1_1_sha256_2048_65537_v3_usr_key.pem\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ "INFO:spsdk.apps.nxpcertgen:Generating Certificate...\n", "INFO:spsdk.apps.nxpcertgen:Loading configuration from yml file...\n", "INFO:spsdk.apps.nxpcertgen:Saving the generated certificate to the specified path...\n", - "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n", - "The certificate file has been created: /Users/macbook-m1/spsdk_test/spsdk/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem\n", - "nxpimage -v mbi export workspace/mbi_config_lpc55s6x.yml\n", - "Success. (Master Boot Image: /Users/macbook-m1/spsdk_test/spsdk/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/lpc55s6x_mbi.bin created.)\n", - "pfr get-template -t cmpa -d lpc55s6x -o workspace/cmpa_lpc55s6x.yml\n", - "WARNING:spsdk.pfr.pfr:The silicon revision is not specified, the latest: '1b' has been used.\n", - "pfr get-template -t cfpa -d lpc55s6x -o workspace/cfpa_lpc55s6x.yml\n", - "WARNING:spsdk.pfr.pfr:The silicon revision is not specified, the latest: '1b' has been used.\n", + "INFO:spsdk.apps.nxpcertgen:Certificate generated successfully...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpimage -v mbi export workspace/mbi_config_lpc55s6x.yml \n", + "Success. (Master Boot Image: c:/repos/spsdk_master/examples/jupyter_examples/lpc55sxx_secure_boot/workspace/lpc55s6x_mbi.bin created.)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:spsdk.image.mbi_mixin:RKTH: ee14c7f74e66cf0b3909c5359af483ed7b73b31e60c2389177fde48194f7651d\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pfr get-template -t cmpa -d lpc55s6x -o workspace/cmpa_lpc55s6x.yml \n", + "PFR cmpa configuration template has been created.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cmpa_lpc55s6x.yml\n", + "pfr get-template -t cfpa -d lpc55s6x -o workspace/cfpa_lpc55s6x.yml \n", + "PFR cfpa configuration template has been created.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa_lpc55s6x.yml\n", "CMPA config:\n", "{ 'description': { 'device': 'lpc55s6x',\n", " 'revision': '1b',\n", " 'type': 'CMPA',\n", - " 'version': '1.7.1',\n", - " 'author': 'NXP',\n", - " 'release': 'alpha'},\n", - " 'settings': { 'BOOT_CFG': { 'bitfields': { 'DEFAULT_ISP_MODE': 'BOOT_CFG_DEFAULT_ISP_MODE_AUTO_ISP',\n", - " 'BOOT_SPEED': 'BOOT_CFG_BOOT_SPEED_SYSTEM_SPEED_CODE',\n", - " 'USB_SPEED': 'BOOT_CFG_USB_SPEED_USB_SPEED_0',\n", - " 'BOOT_FAILURE_PIN': 0}},\n", - " 'SPI_FLASH_CFG': {'bitfields': {'SPI_RECOVERY_BOOT_EN': 0}},\n", - " 'USB_ID': { 'bitfields': { 'USB_VENDOR_ID': 0,\n", - " 'USB_PRODUCT_ID': 0}},\n", - " 'SDIO_CFG': {'value': '0x00000000'},\n", - " 'DCFG_CC_SOCU_PIN': { 'bitfields': { 'NIDEN': 'DCFG_CC_SOCU_PIN_NIDEN_USE_DAP',\n", - " 'DBGEN': 'DCFG_CC_SOCU_PIN_DBGEN_USE_DAP',\n", - " 'SPNIDEN': 'DCFG_CC_SOCU_PIN_SPNIDEN_USE_DAP',\n", - " 'SPIDEN': 'DCFG_CC_SOCU_PIN_SPIDEN_USE_DAP',\n", - " 'TAPEN': 'DCFG_CC_SOCU_PIN_TAPEN_USE_DAP',\n", - " 'MCM33_DBGEN': 'DCFG_CC_SOCU_PIN_MCM33_DBGEN_USE_DAP',\n", - " 'ISP_CMD_EN': 'DCFG_CC_SOCU_PIN_ISP_CMD_EN_USE_DAP',\n", - " 'FA_ME_CMD_EN': 'DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_USE_DAP',\n", - " 'MCM33_NIDEN': 'DCFG_CC_SOCU_PIN_MCM33_NIDEN_USE_DAP',\n", - " 'UUID_CHECK': 0}},\n", - " 'DCFG_CC_SOCU_DFLT': { 'bitfields': { 'NIDEN': 'DCFG_CC_SOCU_DFLT_NIDEN_DISABLED',\n", - " 'DBGEN': 'DCFG_CC_SOCU_DFLT_DBGEN_DISABLED',\n", - " 'SPNIDEN': 'DCFG_CC_SOCU_DFLT_SPNIDEN_DISABLED',\n", - " 'SPIDEN': 'DCFG_CC_SOCU_DFLT_SPIDEN_DISABLED',\n", - " 'TAPEN': 'DCFG_CC_SOCU_DFLT_TAPEN_DISABLED',\n", - " 'MCM33_DBGEN': 'DCFG_CC_SOCU_DFLT_MCM33_DBGEN_DISABLED',\n", - " 'ISP_CMD_EN': 'DCFG_CC_SOCU_DFLT_ISP_CMD_EN_DISABLED',\n", - " 'FA_ME_CMD_EN': 'DCFG_CC_SOCU_DFLT_FA_ME_CMD_EN_DISABLED',\n", - " 'MCM33_NIDEN': 'DCFG_CC_SOCU_DFLT_MCM33_NIDEN_DISABLED'}},\n", - " 'VENDOR_USAGE': {'bitfields': {'VENDOR_USAGE': 0}},\n", - " 'SECURE_BOOT_CFG': { 'bitfields': { 'RSA4K': 'SECURE_BOOT_CFG_RSA4K_RSA2048',\n", - " 'DICE_INC_NXP_CFG': 'SECURE_BOOT_CFG_DICE_INC_NXP_CFG_NOT_INCLUDE',\n", - " 'DICE_CUST_CFG': 'SECURE_BOOT_CFG_DICE_CUST_CFG_NOT_INCLUDE',\n", - " 'SKIP_DICE': 'SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0',\n", - " 'TZM_IMAGE_TYPE': 'SECURE_BOOT_CFG_TZM_IMAGE_TYPE_HEADER',\n", - " 'BLOCK_SET_KEY': 'SECURE_BOOT_CFG_BLOCK_SET_KEY_ALLOW',\n", - " 'BLOCK_ENROLL': 'SECURE_BOOT_CFG_BLOCK_ENROLL_ALLOW',\n", - " 'DICE_INC_SEC_EPOCH': 0,\n", - " 'SEC_BOOT_EN': 'SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0'}},\n", - " 'PRINCE_BASE_ADDR': { 'bitfields': { 'ADDR0_PRG': 0,\n", - " 'ADDR1_PRG': 0,\n", - " 'ADDR2_PRG': 0,\n", - " 'LOCK_REG0': 'PRINCE_BASE_ADDR_LOCK_REG0_UNLOCK',\n", - " 'LOCK_REG1': 'PRINCE_BASE_ADDR_LOCK_REG1_UNLOCK',\n", - " 'REG0_ERASE_CHECK_EN': 'PRINCE_BASE_ADDR_REG0_ERASE_CHECK_EN_DISABLE',\n", - " 'REG1_ERASE_CHECK_EN': 'PRINCE_BASE_ADDR_REG1_ERASE_CHECK_EN_DISABLE',\n", - " 'REG2_ERASE_CHECK_EN': 'PRINCE_BASE_ADDR_REG2_ERASE_CHECK_EN_DISABLE'}},\n", - " 'PRINCE_SR_0': {'value': '0x00000000'},\n", - " 'PRINCE_SR_1': {'value': '0x00000000'},\n", - " 'PRINCE_SR_2': {'value': '0x00000000'},\n", - " 'XTAL_32KHZ_CAPABANK_TRIM': { 'bitfields': { 'TRIM_VALID': 'XTAL_32KHZ_CAPABANK_TRIM_TRIM_VALID_NOT_TRIM',\n", - " 'XTAL_LOAD_CAP_IEC_PF_X100': 0,\n", - " 'PCB_XIN_PARA_CAP_PF_X100': 0,\n", - " 'PCB_XOUT_PARA_CAP_PF_X100': 0}},\n", - " 'XTAL_16MHZ_CAPABANK_TRIM': { 'bitfields': { 'TRIM_VALID': 'XTAL_16MHZ_CAPABANK_TRIM_TRIM_VALID_NOT_TRIM',\n", - " 'XTAL_LOAD_CAP_IEC_PF_X100': 0,\n", - " 'PCB_XIN_PARA_CAP_PF_X100': 0,\n", - " 'PCB_XOUT_PARA_CAP_PF_X100': 0}},\n", - " 'ROTKH': { 'value': '0000000000000000000000000000000000000000000000000000000000000000'},\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'BOOT_CFG': { 'bitfields': { 'BOOT_FAILURE_PIN': '0x00',\n", + " 'BOOT_SPEED': 'SYSTEM_SPEED_CODE',\n", + " 'DEFAULT_ISP_MODE': 'AUTO_ISP',\n", + " 'USB_SPEED': 'USB_SPEED_0'}},\n", " 'CUSTOMER_DEFINED0': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED1': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED10': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED11': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED12': {'value': '0x00000000'},\n", @@ -191,6 +216,7 @@ " 'CUSTOMER_DEFINED17': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED18': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED19': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED20': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED21': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED22': {'value': '0x00000000'},\n", @@ -201,6 +227,7 @@ " 'CUSTOMER_DEFINED27': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED28': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED29': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED30': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED31': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED32': {'value': '0x00000000'},\n", @@ -211,6 +238,7 @@ " 'CUSTOMER_DEFINED37': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED38': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED39': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED40': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED41': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED42': {'value': '0x00000000'},\n", @@ -221,150 +249,166 @@ " 'CUSTOMER_DEFINED47': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED48': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED49': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED50': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED51': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED52': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED53': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED54': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED55': {'value': '0x00000000'}}}\n", - "pfr -v generate-binary -c workspace/cmpa_lpc55s6x.yml -o workspace/cmpa.bin -e workspace/mbi_config_lpc55s6x.yml\n", - "INFO:spsdk.utils.crypto.rkht:ROTKH: afe32dd22b37766ec1b38aff03224bcaee70700a4f796841857587c38596cb84\n", - "nxpdevscan\n", + " 'CUSTOMER_DEFINED55': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", + " 'DCFG_CC_SOCU_DFLT': { 'bitfields': { 'CPU1_DBGEN': 'DISABLED',\n", + " 'CPU1_NIDEN': 'DISABLED',\n", + " 'DBGEN': 'DISABLED',\n", + " 'FA_ME_CMD_EN': 'DISABLED',\n", + " 'ISP_CMD_EN': 'DISABLED',\n", + " 'NIDEN': 'DISABLED',\n", + " 'SPIDEN': 'DISABLED',\n", + " 'SPNIDEN': 'DISABLED',\n", + " 'TAPEN': 'DISABLED'}},\n", + " 'DCFG_CC_SOCU_PIN': { 'bitfields': { 'CPU1_DBGEN': 'USE_DAP',\n", + " 'CPU1_NIDEN': 'USE_DAP',\n", + " 'DBGEN': 'USE_DAP',\n", + " 'FA_ME_CMD_EN': 'USE_DAP',\n", + " 'ISP_CMD_EN': 'USE_DAP',\n", + " 'NIDEN': 'USE_DAP',\n", + " 'SPIDEN': 'USE_DAP',\n", + " 'SPNIDEN': 'USE_DAP',\n", + " 'TAPEN': 'USE_DAP',\n", + " 'UUID_CHECK': 'DISABLED'}},\n", + " 'PRINCE_BASE_ADDR': { 'bitfields': { 'ADDR0_PRG': '0x0',\n", + " 'ADDR1_PRG': '0x0',\n", + " 'ADDR2_PRG': '0x0',\n", + " 'LOCK_REG0': 'UNLOCK',\n", + " 'LOCK_REG1': 'UNLOCK',\n", + " 'REG0_ERASE_CHECK_EN': 'DISABLE',\n", + " 'REG1_ERASE_CHECK_EN': 'DISABLE',\n", + " 'REG2_ERASE_CHECK_EN': 'DISABLE'}},\n", + " 'PRINCE_SR_0': {'value': '0x00000000'},\n", + " 'PRINCE_SR_1': {'value': '0x00000000'},\n", + " 'PRINCE_SR_2': {'value': '0x00000000'},\n", + " 'ROTKH': { 'value': '0000000000000000000000000000000000000000000000000000000000000000'},\n", + " 'SDIO_CFG': {'value': '0x00000000'},\n", + " 'SECURE_BOOT_CFG': { 'bitfields': { 'BLOCK_ENROLL': 'ALLOW',\n", + " 'BLOCK_SET_KEY': 'ALLOW',\n", + " 'DICE_CUST_CFG': 'NOT_INCLUDE',\n", + " 'DICE_INC_NXP_CFG': 'NOT_INCLUDE',\n", + " 'DICE_INC_SEC_EPOCH': '0x0',\n", + " 'RSA4K': 'RSA2048',\n", + " 'SEC_BOOT_EN': 'SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0',\n", + " 'SKIP_DICE': 'SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0',\n", + " 'TZM_IMAGE_TYPE': 'HEADER'}},\n", + " 'SPI_FLASH_CFG': { 'bitfields': { 'SPI_RECOVERY_BOOT_EN': '0x0'}},\n", + " 'USB_ID': { 'bitfields': { 'USB_PRODUCT_ID': '0x0000',\n", + " 'USB_VENDOR_ID': '0x0000'}},\n", + " 'VENDOR_USAGE': {'bitfields': {'VENDOR_USAGE': '0x0000'}},\n", + " 'XTAL_16MHZ_CAPABANK_TRIM': { 'bitfields': { 'PCB_XIN_PARA_CAP_PF_X100': '0x00',\n", + " 'PCB_XOUT_PARA_CAP_PF_X100': '0x00',\n", + " 'TRIM_VALID': 'NOT_TRIM',\n", + " 'XTAL_LOAD_CAP_IEC_PF_X100': '0x00'}},\n", + " 'XTAL_32KHZ_CAPABANK_TRIM': { 'bitfields': { 'PCB_XIN_PARA_CAP_PF_X100': '0x00',\n", + " 'PCB_XOUT_PARA_CAP_PF_X100': '0x00',\n", + " 'TRIM_VALID': 'NOT_TRIM',\n", + " 'XTAL_LOAD_CAP_IEC_PF_X100': '0x00'}}}}\n", + "pfr -v generate-binary -c workspace/cmpa_lpc55s6x.yml -o workspace/cmpa.bin -e workspace/mbi_config_lpc55s6x.yml \n", + "Success. (PFR binary has been generated.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cmpa.bin\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.registers:Bitfield SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0 not found, trying backward compatibility mode with ENABLE_0 (1260ms since start, registers.py:1201)\n", + "WARNING:spsdk.utils.registers:Bitfield SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 not found, trying backward compatibility mode with DISABLE_0 (1260ms since start, registers.py:1201)\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.utils.crypto.rkht:ROTKH: ee14c7f74e66cf0b3909c5359af483ed7b73b31e60c2389177fde48194f7651d\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nxpdevscan \n", "-------- Connected NXP USB Devices --------\n", "\n", - "USB COMPOSITE DEVICE - NXP SEMICONDUCTOR INC.\n", + "LPC-LINK2 CMSIS-DAP V5.224 - NXP Semiconductors\n", "Vendor ID: 0x1fc9\n", - "Product ID: 0x0021\n", - "Path: DevSrvsID:4295541984\n", - "Name: LPC55, RT6xx\n", - "Serial number: \n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_00\\7&36E95AFB&0&0000\n", + "Path Hash: 77e5c391\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPC-LINK2 DATA PORT - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_04\\7&25C3B40A&0&0000\n", + "Path Hash: c297936f\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPCSIO - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Name: \n", + "Serial number: NRA2AQHR\n", "\n", "-------- Connected NXP UART Devices --------\n", "\n", + "Port: COM25\n", + "Type: mboot device\n", + "\n", "-------- Connected NXP SIO Devices --------\n", "\n", - "blhost -u lpc55 get-property current-version\n", + "LIBUSBSIO - NXP Semiconductors, LPCSIO\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Serial number: NRA2AQHR\n", + "Interface number: 3\n", + "Release number: 256\n", + "\n", + "blhost -p com25 get-property current-version \n", "Response status = 0 (0x0) Success.\n", "Response word 1 = 1258487808 (0x4b030000)\n", "Current Version = K3.0.0\n", - "blhost -u lpc55 set-property 29 1\n", + "blhost -p com25 set-property 29 1 \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 write-memory 0x9E600 zero_1536.bin\n", - "\u001b[?25lWriting memory [####################################] 100% \u001b[?25h\n", + "blhost -p com25 write-memory 0x9E600 zero_1536.bin \n", + "Writing memory\n", "Response status = 0 (0x0) Success.\n", "Response word 1 = 1536 (0x600)\n", - "blhost -u lpc55 set-property 29 0\n", + "blhost -p com25 set-property 29 0 \n", "Response status = 0 (0x0) Success.\n", - "pfr read -d lpc55s6x -u lpc55 -t cfpa -o workspace/cfpa.bin -y workspace/cfpa_parsed.yaml --show-diff\n", + "pfr read -d lpc55s6x -p com25 -t cfpa -o workspace/cfpa.bin -y workspace/cfpa_parsed.yaml --show-diff \n", "CFPA page address on lpc55s6x is 0x9de00\n", - "CFPA data stored to workspace/cfpa.bin\n", - "Parsed config stored to workspace/cfpa_parsed.yaml\n", + "CFPA data stored to C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa.bin\n", + "Parsed config stored to C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa_parsed.yaml\n", "Parsed CFPA from the processor\n", "{ 'description': { 'device': 'lpc55s6x',\n", " 'revision': '1b',\n", " 'type': 'CFPA',\n", - " 'version': '1.7.1',\n", - " 'author': 'NXP',\n", - " 'release': 'alpha'},\n", - " 'settings': { 'VERSION': {'value': '0x000007aa'},\n", - " 'ROTKH_REVOKE': { 'bitfields': { 'RoTK0_EN': 'ROTKH_REVOKE_RoTK0_EN_ENABLED'}},\n", - " 'CMPA_PROG_IN_PROGRESS': {'value': '0x5cc55aa5'}}}\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'CMPA_PROG_IN_PROGRESS': {'value': '0x5CC55AA5'},\n", + " 'ROTKH_REVOKE': {'bitfields': {'RoTK0_EN': 'ENABLED'}},\n", + " 'VERSION': {'value': '0x00000002'}}}\n", "CFPA config:\n", "{ 'description': { 'device': 'lpc55s6x',\n", " 'revision': '1b',\n", " 'type': 'CFPA',\n", - " 'version': '1.7.1',\n", - " 'author': 'NXP',\n", - " 'release': 'alpha'},\n", - " 'settings': { 'HEADER': {'value': '0x00000000'},\n", - " 'VERSION': {'value': '0x7ab'},\n", - " 'S_FW_Version': {'value': '0x00000000'},\n", - " 'NS_FW_Version': {'value': '0x00000000'},\n", - " 'IMAGE_KEY_REVOKE': {'value': '0x00000000'},\n", - " 'ROTKH_REVOKE': { 'bitfields': { 'RoTK0_EN': 'ROTKH_REVOKE_RoTK0_EN_ENABLED',\n", - " 'RoTK1_EN': 'ROTKH_REVOKE_RoTK1_EN_INVALID',\n", - " 'RoTK2_EN': 'ROTKH_REVOKE_RoTK2_EN_INVALID',\n", - " 'RoTK3_EN': 'ROTKH_REVOKE_RoTK3_EN_INVALID'}},\n", - " 'VENDOR_USAGE': {'bitfields': {'DBG_VENDOR_USAGE': 0}},\n", - " 'DCFG_CC_SOCU_NS_PIN': { 'bitfields': { 'NIDEN': 'DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP',\n", - " 'DBGEN': 'DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP',\n", - " 'SPNIDEN': 'DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP',\n", - " 'SPIDEN': 'DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP',\n", - " 'TAPEN': 'DCFG_CC_SOCU_NS_PIN_TAPEN_USE_DAP',\n", - " 'MCM33_DBGEN': 'DCFG_CC_SOCU_NS_PIN_MCM33_DBGEN_USE_DAP',\n", - " 'ISP_CMD_EN': 'DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP',\n", - " 'FA_ME_CMD_EN': 'DCFG_CC_SOCU_NS_PIN_FA_ME_CMD_EN_USE_DAP',\n", - " 'MCM33_NIDEN': 'DCFG_CC_SOCU_NS_PIN_MCM33_NIDEN_USE_DAP',\n", - " 'UUID_CHECK': 0}},\n", - " 'DCFG_CC_SOCU_NS_DFLT': { 'bitfields': { 'NIDEN': 'DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED',\n", - " 'DBGEN': 'DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED',\n", - " 'SPNIDEN': 'DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED',\n", - " 'SPIDEN': 'DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED',\n", - " 'TAPEN': 'DCFG_CC_SOCU_NS_DFLT_TAPEN_DISABLED',\n", - " 'MCM33_DBGEN': 'DCFG_CC_SOCU_NS_DFLT_MCM33_DBGEN_DISABLED',\n", - " 'ISP_CMD_EN': 'DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED',\n", - " 'FA_ME_CMD_EN': 'DCFG_CC_SOCU_NS_DFLT_FA_ME_CMD_EN_DISABLED',\n", - " 'MCM33_NIDEN': 'DCFG_CC_SOCU_NS_DFLT_MCM33_NIDEN_DISABLED'}},\n", - " 'ENABLE_FA_MODE': {'value': '0x00000000'},\n", - " 'CMPA_PROG_IN_PROGRESS': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_HEADER0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_HEADER1': { 'bitfields': { 'TYPE': 0,\n", - " 'INDEX': 0,\n", - " 'SIZE': 0}},\n", - " 'PRINCE_REGION0_IV_BODY0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY1': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY2': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY3': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY4': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY5': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY6': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY7': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY8': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY9': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY10': {'value': '0x00000000'},\n", - " 'PRINCE_REGION0_IV_BODY11': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_HEADER0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_HEADER1': { 'bitfields': { 'TYPE': 0,\n", - " 'INDEX': 0,\n", - " 'SIZE': 0}},\n", - " 'PRINCE_REGION1_IV_BODY0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY1': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY2': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY3': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY4': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY5': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY6': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY7': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY8': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY9': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY10': {'value': '0x00000000'},\n", - " 'PRINCE_REGION1_IV_BODY11': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_HEADER0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_HEADER1': { 'bitfields': { 'TYPE': 0,\n", - " 'INDEX': 0,\n", - " 'SIZE': 0}},\n", - " 'PRINCE_REGION2_IV_BODY0': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY1': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY2': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY3': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY4': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY5': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY6': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY7': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY8': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY9': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY10': {'value': '0x00000000'},\n", - " 'PRINCE_REGION2_IV_BODY11': {'value': '0x00000000'},\n", + " 'version': '1.9.1+sp.ear1'},\n", + " 'settings': { 'CMPA_PROG_IN_PROGRESS': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED0': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED1': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED10': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED11': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED12': {'value': '0x00000000'},\n", @@ -375,6 +419,7 @@ " 'CUSTOMER_DEFINED17': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED18': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED19': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED2': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED20': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED21': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED22': {'value': '0x00000000'},\n", @@ -385,6 +430,7 @@ " 'CUSTOMER_DEFINED27': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED28': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED29': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED3': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED30': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED31': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED32': {'value': '0x00000000'},\n", @@ -395,6 +441,7 @@ " 'CUSTOMER_DEFINED37': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED38': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED39': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED4': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED40': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED41': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED42': {'value': '0x00000000'},\n", @@ -405,23 +452,126 @@ " 'CUSTOMER_DEFINED47': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED48': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED49': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED5': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED50': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED51': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED52': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED53': {'value': '0x00000000'},\n", " 'CUSTOMER_DEFINED54': {'value': '0x00000000'},\n", - " 'CUSTOMER_DEFINED55': {'value': '0x00000000'}}}\n", - "pfr -v generate-binary -c workspace/cfpa_lpc55s6x.yml -o workspace/cfpa.bin\n", - "pfr write -u lpc55 -t cfpa -d lpc55s6x -b workspace/cfpa.bin\n", + " 'CUSTOMER_DEFINED55': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED6': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED7': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED8': {'value': '0x00000000'},\n", + " 'CUSTOMER_DEFINED9': {'value': '0x00000000'},\n", + " 'DCFG_CC_SOCU_NS_DFLT': { 'bitfields': { 'CPU1_DBGEN': 'DISABLED',\n", + " 'CPU1_NIDEN': 'DISABLED',\n", + " 'DBGEN': 'DISABLED',\n", + " 'FA_ME_CMD_EN': 'DISABLED',\n", + " 'ISP_CMD_EN': 'DISABLED',\n", + " 'NIDEN': 'DISABLED',\n", + " 'SPIDEN': 'DISABLED',\n", + " 'SPNIDEN': 'DISABLED',\n", + " 'TAPEN': 'DISABLED'}},\n", + " 'DCFG_CC_SOCU_NS_PIN': { 'bitfields': { 'CPU1_DBGEN': 'USE_DAP',\n", + " 'CPU1_NIDEN': 'USE_DAP',\n", + " 'DBGEN': 'USE_DAP',\n", + " 'FA_ME_CMD_EN': 'USE_DAP',\n", + " 'ISP_CMD_EN': 'USE_DAP',\n", + " 'NIDEN': 'USE_DAP',\n", + " 'SPIDEN': 'USE_DAP',\n", + " 'SPNIDEN': 'USE_DAP',\n", + " 'TAPEN': 'USE_DAP',\n", + " 'UUID_CHECK': 'DISABLED'}},\n", + " 'ENABLE_FA_MODE': {'value': '0x00000000'},\n", + " 'HEADER': {'value': '0x00000000'},\n", + " 'IMAGE_KEY_REVOKE': {'value': '0x00000000'},\n", + " 'NS_FW_Version': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION0_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'PRINCE_REGION1_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION1_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'PRINCE_REGION2_IV_BODY0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY1': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY10': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY11': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY2': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY3': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY4': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY5': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY6': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY7': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY8': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_BODY9': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_HEADER0': {'value': '0x00000000'},\n", + " 'PRINCE_REGION2_IV_HEADER1': { 'bitfields': { 'INDEX': '0x0',\n", + " 'SIZE': '0x0',\n", + " 'TYPE': '0x0'}},\n", + " 'ROTKH_REVOKE': { 'bitfields': { 'RoTK0_EN': 'ROTKH_REVOKE_RoTK0_EN_ENABLED',\n", + " 'RoTK1_EN': 'INVALID',\n", + " 'RoTK2_EN': 'INVALID',\n", + " 'RoTK3_EN': 'INVALID'}},\n", + " 'S_FW_Version': {'value': '0x00000000'},\n", + " 'VENDOR_USAGE': { 'bitfields': { 'DBG_VENDOR_USAGE': '0x0000'}},\n", + " 'VERSION': {'value': '0x3'}}}\n", + "pfr -v generate-binary -c workspace/cfpa_lpc55s6x.yml -o workspace/cfpa.bin \n", + "Success. (PFR binary has been generated.\n", + "Result has been stored in: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\cfpa.bin\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:spsdk.utils.registers:Bitfield ROTKH_REVOKE_RoTK0_EN_ENABLED not found, trying backward compatibility mode with ENABLED (1844ms since start, registers.py:1201)\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n", + "INFO:spsdk.pfr.pfrc:OK: Brick condition not fulfilled\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pfr write -p com25 -t cfpa -d lpc55s6x -b workspace/cfpa.bin \n", "CFPA page address on lpc55s6x is 0x9de00\n", "CFPA data written to device.\n", - "pfr write -u lpc55 -t cmpa -d lpc55s6x -b workspace/cmpa.bin\n", + "pfr write -p com25 -t cmpa -d lpc55s6x -b workspace/cmpa.bin \n", "CMPA page address on lpc55s6x is 0x9e400\n", "CMPA data written to device.\n", - "blhost -u lpc55 flash-erase-region 0 0x10000\n", + "blhost -p com25 flash-erase-region 0 0x10000 \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 write-memory 0 workspace/lpc55s6x_mbi.bin\n", - "\u001b[?25lWriting memory [####################################] 100% \u001b[?25h\n", + "blhost -p com25 write-memory 0 workspace/lpc55s6x_mbi.bin \n", + "Writing memory\n", "Response status = 0 (0x0) Success.\n", "Response word 1 = 11064 (0x2b38)\n" ] @@ -442,16 +592,17 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "nxpimage sb21 get-sbkek -o workspace/\n", - "SBKEK: 05401b7e5c003b75c69df91999e0fd1eeb3a8cf46686ba9974fdb3c94bc2083d\n", - "SBKEK has been stored to: workspace/\n" + "nxpimage sb21 get-sbkek -o workspace/ \n", + "SBKEK: af39c2f536cdd56b91a6b64334672fba08200fcdcf7715e40884a855742101e5\n", + "(OTP) MASTER KEY: e8df82db763cb8ae442097665ed9524d1ab7187515789bebce3f34dc36a7e196\n", + "Keys have been stored to: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\n" ] } ], @@ -477,15 +628,15 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "nxpimage -v sb21 export -k workspace/sbkek.txt -c workspace/signed.bd -o workspace/output.sb2 -s workspace/rsa2048_key.pem -S workspace/ROT1_sha256_2048_65537_v3_ca_crt.der -S workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem -R workspace/ROT1_sha256_2048_65537_v3_ca_crt.der workspace/lpc55s6x_mbi.bin\n", - "Success. (Secure binary 2.1: workspace/output.sb2 created.)\n" + "nxpimage -v sb21 export -k workspace/sbkek.txt -c workspace/signed.bd -o workspace/output.sb2 -s workspace/rsa2048_key.pem -S workspace/ROT1_sha256_2048_65537_v3_ca_crt.der -S workspace/IMG1_1_sha256_2048_65537_v3_usr_key.pem -R workspace/ROT1_sha256_2048_65537_v3_ca_crt.der workspace/lpc55s6x_mbi.bin \n", + "Success. (Secure binary 2.1: C:\\repos\\spsdk_master\\examples\\jupyter_examples\\lpc55sxx_secure_boot\\workspace\\output.sb2 created.)\n" ] } ], @@ -522,7 +673,6 @@ "assert os.path.exists(SBKEK_BIN)\n", "\n", "%! nxpimage $VERBOSITY sb21 export -k $SBKEK_PATH -c $BD_FILE_PATH -o $SB2_PATH -s $PRIVATE_KEY_PATH -S $ROOT_0_CERT_PATH -S $CHAIN_CERT_0 -R $ROOT_0_CERT_PATH $BIN_OUTPUT_PATH\n", - "assert _exit_code == 0\n", "\n", "assert os.path.exists(SB2_PATH)\n" ] @@ -548,26 +698,55 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "nxpdevscan\n", + "nxpdevscan \n", "-------- Connected NXP USB Devices --------\n", "\n", - "USB COMPOSITE DEVICE - NXP SEMICONDUCTOR INC.\n", + "LPC-LINK2 CMSIS-DAP V5.224 - NXP Semiconductors\n", "Vendor ID: 0x1fc9\n", - "Product ID: 0x0021\n", - "Path: DevSrvsID:4295541984\n", - "Name: LPC55, RT6xx\n", - "Serial number: \n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_00\\7&36E95AFB&0&0000\n", + "Path Hash: 77e5c391\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPC-LINK2 DATA PORT - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_04\\7&25C3B40A&0&0000\n", + "Path Hash: c297936f\n", + "Name: \n", + "Serial number: NRA2AQHR\n", + "\n", + "LPCSIO - NXP Semiconductors\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Name: \n", + "Serial number: NRA2AQHR\n", "\n", "-------- Connected NXP UART Devices --------\n", "\n", + "Port: COM25\n", + "Type: mboot device\n", + "\n", "-------- Connected NXP SIO Devices --------\n", + "\n", + "LIBUSBSIO - NXP Semiconductors, LPCSIO\n", + "Vendor ID: 0x1fc9\n", + "Product ID: 0x0090\n", + "Path: HID\\VID_1FC9&PID_0090&MI_03\\7&38F3D048&0&0000\n", + "Path Hash: 5f15a706\n", + "Serial number: NRA2AQHR\n", + "Interface number: 3\n", + "Release number: 256\n", "\n" ] } @@ -579,14 +758,14 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "blhost -u lpc55 get-property current-version\n", + "blhost -p com25 get-property current-version \n", "Response status = 0 (0x0) Success.\n", "Response word 1 = 1258487808 (0x4b030000)\n", "Current Version = K3.0.0\n" @@ -596,14 +775,13 @@ "source": [ "USB_CONNECTION = \"-u lpc55\" \n", "# choose com port or /dev\n", - "UART_CONNECTION = \"-p com6\"\n", + "UART_CONNECTION = \"-p com25\"\n", "\n", "# comment if you want to use UART\n", - "CONNECTION = USB_CONNECTION\n", + "CONNECTION = UART_CONNECTION\n", "# CONNECTION = UART_CONNECTION\n", "\n", - "%! blhost $CONNECTION get-property current-version\n", - "assert _exit_code == 0" + "%! blhost $CONNECTION get-property current-version" ] }, { @@ -619,30 +797,30 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "blhost -u lpc55 read-memory 0x0009E600 4\n", - "\u001b[?25lReading memory [####################################] 100%\u001b[?25h\n", + "blhost -p com25 read-memory 0x0009E600 4 \n", + "Reading memory\n", "00 00 00 00\n", "Response status = 0 (0x0) Success.\n", "Response word 1 = 4 (0x4)\n", "Read 4 of 4 bytes.\n", - "blhost -u lpc55 key-provisioning enroll\n", + "blhost -p com25 key-provisioning enroll \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 key-provisioning set_user_key 3 workspace/sbkek.bin\n", + "blhost -p com25 key-provisioning set_user_key 3 workspace/sbkek.bin \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 key-provisioning set_key 7 16\n", + "blhost -p com25 key-provisioning set_key 7 16 \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 key-provisioning set_key 8 16\n", + "blhost -p com25 key-provisioning set_key 8 16 \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 key-provisioning set_key 9 16\n", + "blhost -p com25 key-provisioning set_key 9 16 \n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 key-provisioning write_key_nonvolatile 0\n", + "blhost -p com25 key-provisioning write_key_nonvolatile 0 \n", "Response status = 0 (0x0) Success.\n" ] } @@ -653,23 +831,18 @@ "\n", "## Key provisioning enroll uncomment in case it's not enrolled ###\n", "%! blhost $CONNECTION key-provisioning enroll\n", - "assert _exit_code == 0\n", "\n", "### Write KEK for SB \n", "%! blhost $CONNECTION key-provisioning set_user_key 3 $SBKEK_BIN\n", - "assert _exit_code == 0\n", "\n", "### Generate random key for PRINCE region 0 ###\n", "%! blhost $CONNECTION key-provisioning set_key 7 16\n", - "assert _exit_code == 0\n", "\n", "### Generate random key for PRINCE region 1 ###\n", "%! blhost $CONNECTION key-provisioning set_key 8 16\n", - "assert _exit_code == 0\n", "\n", "### Generate random key for PRINCE region 2 ###\n", "%! blhost $CONNECTION key-provisioning set_key 9 16\n", - "assert _exit_code == 0\n", "\n", "### In case the key store has been enrolled comment if enrolling\n", "# read keystore from internal flash\n", @@ -679,8 +852,7 @@ "# %! blhost $CONNECTION key-provisioning set_user_key 3 $SBKEK_BIN\n", "\n", "# ### Now write the keystore to internal flash\n", - "%! blhost $CONNECTION key-provisioning write_key_nonvolatile 0\n", - "assert _exit_code == 0\n" + "%! blhost $CONNECTION key-provisioning write_key_nonvolatile 0\n" ] }, { @@ -693,28 +865,26 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "blhost -u lpc55 receive-sb-file workspace/output.sb2\n", - "\u001b[?25lSending SB file [####################################] 100% \u001b[?25h\n", + "blhost -p com25 receive-sb-file workspace/output.sb2 \n", + "Sending SB file\n", "Response status = 0 (0x0) Success.\n", - "blhost -u lpc55 reset\n", + "blhost -p com25 reset \n", "Response status = 0 (0x0) Success.\n" ] } ], "source": [ "%! blhost $CONNECTION receive-sb-file $SB2_PATH\n", - "assert _exit_code == 0\n", "\n", "# reset target\n", - "%! blhost $CONNECTION reset\n", - "assert _exit_code == 0" + "%! blhost $CONNECTION reset" ] } ], @@ -737,7 +907,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.9" + "version": "3.9.0" }, "orig_nbformat": 4 }, diff --git a/examples/lpc55xx_tz_pfr.py b/examples/lpc55xx_tz_pfr.py index af9f1f1c..eeb64a57 100644 --- a/examples/lpc55xx_tz_pfr.py +++ b/examples/lpc55xx_tz_pfr.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -16,6 +16,8 @@ from spsdk.image import TrustZone from spsdk.pfr import CFPA +from spsdk.pfr.pfr import PfrConfiguration +from spsdk.utils.misc import load_configuration # Uncomment for printing debug messages # import logging @@ -23,6 +25,7 @@ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(THIS_DIR, "data") +CFPA_TEST_FILE = os.path.join(DATA_DIR, "cfpa_lpc55s6x_test.yaml") def generate_trustzone() -> None: @@ -53,10 +56,8 @@ def generate_pfr() -> None: !!! Caution !!! Incorrectly configured data may lock the device from further use """ - with open(os.path.join(DATA_DIR, "cfpa_test.json")) as config_file: - config_data = json.load(config_file) - - cfpa = CFPA("lpc55xx", user_config=config_data) + config_data = PfrConfiguration(load_configuration(CFPA_TEST_FILE)) + cfpa = CFPA("lpc55s6x", revision="1b", user_config=config_data) cfpa_data = cfpa.export() with open(os.path.join(THIS_DIR, "cfpa.bin"), "wb") as binary_file: diff --git a/examples/sdp_mboot.py b/examples/sdp_mboot.py index 0129e879..acb483bb 100644 --- a/examples/sdp_mboot.py +++ b/examples/sdp_mboot.py @@ -31,7 +31,7 @@ def run_flash_loader( :param data: flashloader binary data :param device_name: i.MX-RT device name or VID:PID :return: True if running flashloader was successful - :raise sdp.SdpError: If SDP operation fails + :raises sdp.SdpError: If SDP operation fails """ devices = sdp.scan_usb(device_name) if not devices: diff --git a/pyproject.toml b/pyproject.toml index 000aac71..cd1b4154 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length = 100 -target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] +target-version = ['py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' force-exclude = ''' ''' diff --git a/release_notes.txt b/release_notes.txt index fadf1184..550b3092 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -7,28 +7,35 @@ configure the device, prepare, download and upload data, including security oper delivered in the form of a python library and command-line applications. -Version: 1.9.1 - +Version: 1.10.0 ============== -Date: 17-March-2023 +Date: 5-April-2023 New features -- nxpdevhsm - - split reset option in nxpdevhsm into two; disable init reset by default - -Bugfixes - +- blhost: + - add new command: ele_message - nxpdebugmbox: - - fix Linux error on PyOCD - - fix PyOCD and PEmicro connection for kw45xx and k32w1xx + - add command: read UUID from device + - update PyOCD to latest version to support CMSIS DAP FW v3 - nxpdevhsm: - - fix buffer base address for DevHSM operations + - USER_PCK rename to CUST_MK_SK - nxpimage: - - fix handling exception when the root cert index is wrong - -- tphost/tpconfig: - - incorrect output in TP PG command in case of an failure - + - add subcommand group for generate and parse certificate block + - replace private key to signature provider in master boot image + - OTFAD support for RT1170 +- ifr: + - add commands read/write +- pfr: + - add CMPA erase command +Bugfixes +- nxpdebugmbox: + - fix AP selection issue for PyOCD and PEMICRO + - fix DAC verification when there is only 1 root key +- nxpimage: + - fix MBI issue with HMAC +- shadowregs: + - fix endianness for OTP MASTER KEY +- drop support for Python 3.7 Supported devices ================= @@ -50,11 +57,22 @@ System Requirements Supported Environment ===================== -Python 3.7+ interpreter, old version 2.x is not supported +Python 3.8+ interpreter, old version 2.x is not supported Revision History ================ +1.9.1 +- nxpdebugmbox: + - fix Linux error on PyOCD + - fix PyOCD and PEmicro connection for kw45xx and k32w1xx +- nxpdevhsm: + - fix buffer base address for DevHSM operations + - split reset option in nxpdevhsm into two; disable init reset by default +- nxpimage: + - fix handling exception when the root cert index is wrong +- tphost/tpconfig: + - incorrect output in TP PG command in case of an failure 1.9.0 - nxpdebugmbox: - add check of root of trust hash in dat authentication diff --git a/requirements-develop.txt b/requirements-develop.txt index d7ce7a36..9f4f8bd3 100644 --- a/requirements-develop.txt +++ b/requirements-develop.txt @@ -1,34 +1,33 @@ # tox has to be installed first -tox<4.3 +tox<4.5 -r requirements.txt # testing pytest<7.3 pytest-cov<4.1 -pytest-xdist<3.2 -pyyaml<5.5 +pytest-xdist<3.3 voluptuous<0.14 # metrics radon<5.2 -mypy>=0.800,<0.992 +mypy>=0.800,<1.2 types-requests>=2.26,<2.29 -types-setuptools<65.8 +types-setuptools<67.7 types-pyyaml<6.1 # codestyle -pylint>=2.6.0,<2.16 -pydocstyle[toml]<6.3 -black<22.13 +pylint>=2.6.0,<2.18 +pydocstyle[toml]<6.4 +black<23.2 # cli executables -pyinstaller<5.8 -pyinstaller-hooks-contrib<2022.15 +pyinstaller<5.10 +pyinstaller-hooks-contrib<2023.2 # developement/CI tools bump2version<1.1 -pre-commit<2.22 -openpyxl<3.1 +pre-commit<3.3 +openpyxl<3.2 cachier<2.1 -jira<3.5 +jira<3.6 inquirerpy<0.4 # examples flask<2.3 requests<2.29 -ipython<8.9 +ipython<8.12 notebook<6.6 diff --git a/requirements.txt b/requirements.txt index a696e46e..11331d6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ asn1crypto>=1.2,<1.6 astunparse>=1.6,<1.7 -bincopy>=17.10.2,<=17.14.0 +bincopy>=17.14.5,<17.15 bitstring>=3.1,<4.1 click-option-group>=0.3.0,<0.6 click-command-tree<1.2 @@ -8,22 +8,19 @@ click>=7.1,<8.2 colorama>=0.4.6,<0.5 commentjson>=0.9,<0.10 crcmod<1.8 -cryptography>=3.4.4,<39.1 +cryptography>=3.4.4,<40.1 deepmerge<1.2 fastjsonschema>=2.15.1,<2.17 hexdump<3.4 jinja2>=3.0,<3.2 libusbsio>=2.1.11,<2.2 oscrypto<1.4 -pycryptodome>=3.9.3,<3.17 +pycryptodome>=3.9.3,<3.18 pylink-square>=0.8.2,<0.15 pyocd-pemicro>=1.1.5,<1.2 -pyocd>=0.28.3,<0.32 +pyocd>=0.33.0,<0.35 pypemicro>=0.1.11,<0.2 pyserial>=3.1,<3.6 ruamel.yaml>=0.17,<0.18 sly<0.6 -# There's an issue with 0.3.0 on win -# https://github.com/pyocd/cmsis-pack-manager/issues/176 -cmsis-pack-manager<0.3.0 -typing-extensions<4.5 +typing-extensions<4.6 diff --git a/setup.py b/setup.py index 1a87e192..48bb9782 100644 --- a/setup.py +++ b/setup.py @@ -38,14 +38,13 @@ long_description=long_description, long_description_content_type="text/markdown", platforms="Windows, Linux, Mac OSX", - python_requires=">=3.7", + python_requires=">=3.8", setup_requires=["setuptools>=40.0"], install_requires=requirements, include_package_data=True, classifiers=[ "Development Status :: 3 - Alpha", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -59,7 +58,7 @@ "Topic :: System :: Hardware", "Topic :: Utilities", ], - packages=find_packages(exclude=["tests.*", "tests", "examples.*", "examples"]), + packages=find_packages(exclude=["tests.*", "tests", "examples.*", "examples", "tools", "tools.*"]), entry_points={ "console_scripts": [ "elftosb=spsdk.apps.elftosb:safe_main", diff --git a/spsdk/__version__.py b/spsdk/__version__.py index d80bd2f0..779beea8 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.9.1" +__version__ = "1.10.0" diff --git a/spsdk/apps/blhost.py b/spsdk/apps/blhost.py index 86de5833..ae8025c0 100644 --- a/spsdk/apps/blhost.py +++ b/spsdk/apps/blhost.py @@ -39,6 +39,7 @@ catch_spsdk_error, format_raw_data, get_interface, + get_key, parse_file_and_size, parse_hex_data, ) @@ -117,8 +118,6 @@ def batch(ctx: click.Context, command_file: str) -> None: \b COMMAND_FILE - path to blhost command file """ - click.secho("This is an experimental command. Use at your own risk!", fg="yellow") - with open(command_file) as f: for line in f.readlines(): tokes = shlex.split(line, comments=True) @@ -326,7 +325,6 @@ def flash_image(ctx: click.Context, image_file_path: str, erase: str, memory_id: with McuBoot(ctx.obj["interface"]) as mboot: if erase == "erase": for segment in bin_image.sub_images: - mboot.flash_erase_region( address=segment.aligned_start(ALIGNMENT), length=segment.aligned_length(ALIGNMENT), @@ -963,16 +961,33 @@ def program_aeskey(ctx: click.Context, file: click.File) -> None: @key_provisioning.command(name="set_user_key") +@click.option( + "-s", + "--key-size", + type=INT(), + required=False, + help=( + "Key size in bits. If this field is defined," + " the command could load as text as binary form of key." + ), +) @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: str, file_and_size: str) -> None: +def set_user_key(ctx: click.Context, key_type: str, file_and_size: str, key_size: int) -> None: """Sends the user key specified by type to the bootloader. is the binary file containing user key plain text. If is not specified, the entire will be sent. Otherwise, blhost only sends the first bytes. + \b + If the '--key-size' option is defined, the argument 'file_and_size' accepts those formats: + - filename to binary file + - filename to text file + - string with key + The size part is ignored in this case. + \b TYPE - Type of user key FILE - Binary file containing user key plaintext @@ -996,8 +1011,11 @@ def set_user_key(ctx: click.Context, key_type: str, file_and_size: str) -> None: """ 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) + + if not key_size: + key_size = (size if size > 0 else os.path.getsize(file_path)) * 8 + + key_data = get_key(file_path, expected_size=key_size // 8) with McuBoot(ctx.obj["interface"]) as mboot: mboot.kp_set_user_key(key_type=key_type_int, key_data=key_data) # type: ignore @@ -1456,7 +1474,7 @@ def oem_get_cust_cert_dice_puk( """Creates the initial trust provisioning keys. \b - OEM_RKTH_INPUT_ADDR - The input buffer address where the OEM RKTH locates at + OEM_RKTH_INPUT_ADDR - The input buffer address where the OEM RKTH locates at OEM_RKTH_INPUT_SIZE - The byte count of the OEM RKTH OEM_CUST_CERT_DICE_PUK_OUTPUT_ADDR - The output buffer address where ROM writes the OEM Customer Certificate Public Key for DICE to @@ -1498,6 +1516,41 @@ def update_life_cycle(ctx: click.Context, life_cycle: int) -> None: display_output([], mboot.status_code, ctx.obj["use_json"], ctx.obj["silent"]) +@main.command() +@click.argument("cmd-msg-addr", metavar="COMMAND MESSAGE ADDRESS", type=INT(), required=True) +@click.argument("cmd-msg-cnt", metavar="COMMAND MESSAGE COUNT", type=INT(), required=True) +@click.argument("resp-msg-addr", metavar="RESPONSE MESSAGE ADDRESS", type=INT(), required=True) +@click.argument("resp-msg-cnt", metavar="RESPONSE MESSAGE COUNT", type=INT(), required=True) +@click.pass_context +def ele_message( + ctx: click.Context, cmd_msg_addr: int, cmd_msg_cnt: int, resp_msg_addr: int, resp_msg_cnt: int +) -> None: + """Send message to EdgeLock Enclave. + + This command is designed to be, as general, as is possible to work with EdgeLock Enclave. + EdgeLock Enclave message is prepared in PC and stored in target RAM (for example by 'blhost write-memory'). + The response of ELE command is stored also in target memory on place that is defined by 'resp-msg-addr + and could be read back (for example by 'blhost read-memory'). + + + Size of command message and response is in count of 32-bit words. + + \b + COMMAND MESSAGE ADDRESS - Address in target where is stored the command words. + COMMAND MESSAGE COUNT - Count of the stored command words. + RESPONSE MESSAGE ADDRESS - Address in target memory space where the ELE store response. + RESPONSE MESSAGE COUNT - Maximal count of words reserved for response. + """ + with McuBoot(ctx.obj["interface"]) as mboot: + mboot.ele_message( + cmdMsgAddr=cmd_msg_addr, + cmdMsgCnt=cmd_msg_cnt, + respMsgAddr=resp_msg_addr, + respMsgCnt=resp_msg_cnt, + ) + display_output([], mboot.status_code, ctx.obj["use_json"], ctx.obj["silent"]) + + @trust_provisioning.command(name="prove_genuinity") @click.argument("address", type=INT()) @click.argument("buffer_size", type=INT()) diff --git a/spsdk/apps/elftosb.py b/spsdk/apps/elftosb.py index 3fa16d77..925d3ca0 100644 --- a/spsdk/apps/elftosb.py +++ b/spsdk/apps/elftosb.py @@ -16,10 +16,10 @@ from spsdk import __version__ as spsdk_version from spsdk.apps.utils.utils import SPSDKAppError, catch_spsdk_error from spsdk.exceptions import SPSDKError -from spsdk.image import SB3_SCH_FILE, TrustZone, get_mbi_class +from spsdk.image import TrustZone, get_mbi_class from spsdk.image.mbimg import mbi_generate_config_templates, mbi_get_supported_families from spsdk.sbfile.sb2.images import generate_SB21 -from spsdk.sbfile.sb31.images import SecureBinary31 +from spsdk.sbfile.sb31.images import SB3_SCH_FILE, SecureBinary31 from spsdk.utils.misc import load_configuration, write_file from spsdk.utils.schema_validator import ValidationSchemas, check_config @@ -33,7 +33,7 @@ def generate_trustzone_binary(tzm_conf: str) -> None: check_config(config_data, TrustZone.get_validation_schemas(config_data["family"])) trustzone = TrustZone.from_config(config_data) tz_data = trustzone.export() - output_file = config_data["tzpOutputFile"] + output_file = os.path.abspath(config_data["tzpOutputFile"]) write_file(tz_data, output_file, mode="wb") click.echo(f"Success. (Trustzone binary: {output_file} created.)") @@ -53,18 +53,17 @@ def generate_config_templates(family: str, output_folder: str) -> None: # And generate all config templates files for key, val in templates.items(): - file_name = f"{key}.yml" + file_name = f"{key}.yaml" if os.path.isfile(output_folder): raise SPSDKError(f"The specified path {output_folder} is file.") if not os.path.isdir(output_folder): os.mkdir(output_folder) - full_file_name = os.path.join(output_folder, file_name) + full_file_name = os.path.abspath(os.path.join(output_folder, file_name)) if not os.path.isfile(full_file_name): click.echo(f"Creating {file_name} template file.") - with open(full_file_name, "w") as f: - f.write(val) + write_file(val, full_file_name) else: - click.echo(f"Skip creating {file_name}, this file already exists.") + click.echo(f"Skip creating {full_file_name}, this file already exists.") def generate_master_boot_image(image_conf: str) -> None: @@ -79,7 +78,7 @@ def generate_master_boot_image(image_conf: str) -> None: mbi.load_from_config(config_data) mbi_data = mbi.export() - mbi_output_file_path = config_data["masterBootOutputFile"] + mbi_output_file_path = os.path.abspath(config_data["masterBootOutputFile"]) write_file(mbi_data, mbi_output_file_path, mode="wb") click.echo(f"Success. (Master Boot Image: {mbi_output_file_path} created.)") @@ -125,7 +124,8 @@ def generate_secure_binary_21( hoh_out_path=str(hoh_out_path), external_files=[str(x) for x in external_files], ) - write_file(sb2_data, str(output_file_path), mode="wb") + output_file_path = os.path.abspath(output_file_path) + write_file(sb2_data, output_file_path, mode="wb") except SPSDKError as exc: raise SPSDKAppError(f"The SB2.1 file generation failed: ({str(exc)}).") from exc else: @@ -141,13 +141,16 @@ def generate_secure_binary_31(container_conf: str) -> None: :raises SPSDKError: Raised when there is no signing key """ config_data = load_configuration(container_conf) - schemas = SecureBinary31.get_validation_schemas(include_test_configuration=True) + check_config(config_data, SecureBinary31.get_validation_schemas_family()) + schemas = SecureBinary31.get_validation_schemas( + config_data["family"], include_test_configuration=True + ) schemas.append(ValidationSchemas.get_schema_file(SB3_SCH_FILE)["sb3_output"]) check_config(config_data, schemas) sb3 = SecureBinary31.load_from_config(config_data) - sb3_data = sb3.export() + sb3_data = sb3.export(cert_block=None) - sb3_output_file_path = config_data["containerOutputFile"] + sb3_output_file_path = os.path.abspath(config_data["containerOutputFile"]) write_file(sb3_data, sb3_output_file_path, mode="wb") click.echo(f"Success. (Secure binary 3.1: {sb3_output_file_path} created.)") diff --git a/spsdk/apps/elftosb_utils/sb_31_helper.py b/spsdk/apps/elftosb_utils/sb_31_helper.py index dbb85cee..4b9d9d53 100644 --- a/spsdk/apps/elftosb_utils/sb_31_helper.py +++ b/spsdk/apps/elftosb_utils/sb_31_helper.py @@ -11,7 +11,7 @@ from spsdk import SPSDKError from spsdk.image import MBIMG_SCH_FILE from spsdk.utils.crypto import CRYPTO_SCH_FILE -from spsdk.utils.crypto.cert_blocks import get_main_cert_index +from spsdk.utils.crypto.cert_blocks import find_root_certificates, get_main_cert_index from spsdk.utils.schema_validator import ValidationSchemas, check_config @@ -29,9 +29,7 @@ def __init__(self, data: dict, search_paths: Optional[List[str]] = None) -> None sch_cfg = ValidationSchemas.get_schema_file(MBIMG_SCH_FILE) sch_crypto_cfg = ValidationSchemas.get_schema_file(CRYPTO_SCH_FILE) val_schemas = [sch_cfg[x] for x in ["cert_prv_key", "signing_root_prv_key"]] - val_schemas.extend( - [sch_crypto_cfg[x] for x in ["certificate_v2_chain_id", "certificate_root_keys"]] - ) + val_schemas.append(sch_crypto_cfg["certificate_root_keys"]) check_config(data, val_schemas, search_paths=search_paths) self.config_data = data @@ -42,15 +40,8 @@ def __init__(self, data: dict, search_paths: Optional[List[str]] = None) -> None raise SPSDKError( "Private key not specified (mainCertPrivateKeyFile or mainRootCertPrivateKeyFile)" ) - public_keys: List[Optional[str]] = [ - data.get(f"rootCertificate{idx}File") for idx in range(4) - ] - # filter out None and empty values - self.public_keys = list(filter(None, public_keys)) - for org, filtered in zip(public_keys, self.public_keys): - if org != filtered: - raise SPSDKError("There are gaps in rootCertificateXFile definition") - data_main_cert_index = get_main_cert_index(data) + self.public_keys = find_root_certificates(data) + data_main_cert_index = get_main_cert_index(data, search_paths=search_paths) root_cert_file = data.get(f"rootCertificate{data_main_cert_index}File") if not root_cert_file: raise SPSDKError(f"rootCertificate{data_main_cert_index}File doesn't exist") diff --git a/spsdk/apps/ifr.py b/spsdk/apps/ifr.py index d85bb778..c5eafbe9 100644 --- a/spsdk/apps/ifr.py +++ b/spsdk/apps/ifr.py @@ -13,26 +13,58 @@ import click -from spsdk import pfr from spsdk.apps.utils.common_cli_options import ( FC, CommandsTreeGroupAliasedGetCfgTemplate, + isp_interfaces, spsdk_apps_common_options, ) -from spsdk.apps.utils.utils import catch_spsdk_error +from spsdk.apps.utils.utils import catch_spsdk_error, format_raw_data, get_interface +from spsdk.exceptions import SPSDKError +from spsdk.mboot import McuBoot +from spsdk.mboot.interfaces.base import MBootInterface +from spsdk.pfr import pfr from spsdk.pfr.exceptions import SPSDKPfrConfigError from spsdk.pfr.pfr import ROMCFG -from spsdk.utils.misc import write_file +from spsdk.utils.misc import load_binary, size_fmt, write_file from spsdk.utils.schema_validator import ConfigTemplate logger = logging.getLogger(__name__) -def _store_output(data: Union[str, bytes], path: Optional[str], mode: str = "w") -> None: +def _parse_binary_data( + data: bytes, + device: str, + revision: Optional[str] = None, + show_calc: bool = False, + show_diff: bool = False, +) -> str: + """Parse binary data and extract YAML configuration. + + :param data: Data to parse + :param device: Device to use + :param revision: Revision to use, defaults to 'latest' + :param show_calc: Also show calculated fields + :param show_diff: Show only difference to default + :return: IFR YAML configuration as a string + """ + ifr_obj = ROMCFG(device=device, revision=revision) + ifr_obj.parse(data) + parsed = ifr_obj.get_yaml_config(exclude_computed=not show_calc, diff=show_diff) + yaml_data = ConfigTemplate.convert_cm_to_yaml(parsed) + return yaml_data + + +def _store_output( + data: Union[str, bytes], path: Optional[str], mode: str = "w", msg: Optional[str] = None +) -> None: """Store the output data; either on stdout or into file if it's provided.""" + if msg: + click.echo(msg) if path is None: click.echo(data) else: + click.echo(f"Result has been stored in: {path}") write_file(data, path=path, mode=mode) @@ -72,7 +104,7 @@ def devices() -> None: @click.option( "-o", "--output", - type=str, + type=click.Path(resolve_path=True), required=False, help="Save the output into a file instead of console", ) @@ -82,7 +114,7 @@ def get_template(device: str, revision: str, output: str, full: bool) -> None: ifr_obj = ROMCFG(device=device, revision=revision) data = ifr_obj.get_yaml_config(not full) yaml_data = ConfigTemplate.convert_cm_to_yaml(data) - _store_output(yaml_data, output) + _store_output(yaml_data, output, msg=f"IFR configuration template has been created.") @main.command(name="parse-binary", no_args_is_help=True) @@ -90,22 +122,26 @@ def get_template(device: str, revision: str, output: str, full: bool) -> None: @click.option( "-o", "--output", - type=str, + type=click.Path(resolve_path=True), 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( + "-b", + "--binary", + type=click.Path(exists=True, resolve_path=True), + required=True, + help="Binary to parse", +) @click.option("-f", "--show-diff", is_flag=True, help="Show differences comparing to defaults") def parse_binary(revision: str, output: str, binary: str, show_diff: bool, device: str) -> None: """Parse binary and extract configuration.""" ifr_obj = ROMCFG(device=device, revision=revision) - data = binary.read() # type: ignore + data = load_binary(binary) ifr_obj.parse(data) parsed = ifr_obj.get_yaml_config(exclude_computed=False, diff=show_diff) yaml_data = ConfigTemplate.convert_cm_to_yaml(parsed) - _store_output(yaml_data, output) - - click.echo(f"Success. (IFR: {binary} has been parsed and stored into {output}.)") + _store_output(yaml_data, output, msg=f"Success. (IFR: {binary} has been parsed.") @main.command(name="generate-binary", no_args_is_help=True) @@ -120,14 +156,14 @@ def parse_binary(revision: str, output: str, binary: str, show_diff: bool, devic "-c", "--user-config", "user_config_file", - type=str, + type=click.Path(exists=True, resolve_path=True), required=True, help="YAML/JSON file with user configuration", ) @click.option( "-o", "--output", - type=str, + type=click.Path(resolve_path=True), required=True, help="Save the output into a file instead of console", ) @@ -147,9 +183,141 @@ def generate_binary(output: str, user_config_file: str, device: str) -> None: ifr_obj.set_config(ifr_config, raw=False) data = ifr_obj.export() - _store_output(data, output, "wb") + _store_output(data, output, "wb", msg=f"Success. (IFR binary has been generated)") + + +@main.command(name="write", no_args_is_help=True) +@isp_interfaces( + uart=True, + usb=True, + lpcusbsio=True, + buspal=True, + json_option=False, + use_long_timeout_option=True, +) +@ifr_device_type_options +@click.option( + "-b", + "--binary", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + required=True, + help="Path to IFR data to write.", +) +def write( + port: str, + usb: str, + buspal: str, + lpcusbsio: str, + timeout: int, + device: str, + revision: str, + binary: str, +) -> None: + """Write IFR page to the device.""" + ifr_obj = ROMCFG(device=device, revision=revision) + ifr_page_address = ifr_obj.config.get_address(device) + ifr_page_length = ifr_obj.BINARY_SIZE + + click.echo(f"{ifr_obj.__class__.__name__} page address on {device} is {ifr_page_address:#x}") + + data = load_binary(binary) + if len(data) != ifr_page_length: + raise SPSDKError( + f"IFR page length is {ifr_page_length}. Provided binary has {size_fmt(len(data))}." + ) - click.echo(f"Success. (IFR binary has been generated into {output}.)") + interface = get_interface( + module="mboot", port=port, usb=usb, buspal=buspal, lpcusbsio=lpcusbsio, timeout=timeout + ) + assert isinstance(interface, MBootInterface) + with McuBoot(device=interface) as mboot: + mboot.write_memory(address=ifr_page_address, data=data) + click.echo( + f"{ifr_obj.__class__.__name__} data {'written to device.' if mboot.status_code == 0 else 'write failed!'}" + ) + + +@main.command(name="read", no_args_is_help=True) +@isp_interfaces( + uart=True, + usb=True, + lpcusbsio=True, + buspal=True, + json_option=False, + use_long_timeout_option=True, +) +@ifr_device_type_options +@click.option( + "-o", + "--output", + type=click.Path(dir_okay=False, resolve_path=True), + help="Store IFR data into a file. If not specified hexdump data into stdout.", +) +@click.option( + "-y", + "--yaml", + "yaml_output", + type=click.Path(dir_okay=False, resolve_path=True), + help="Parse data read from device into YAML config.", +) +@click.option( + "-f", + "--show-diff", + is_flag=True, + help="(applicable for parsing) Show differences comparing to defaults", +) +@click.option( + "-c", + "--show-calc", + is_flag=True, + help="(applicable for parsing) Show also calculated fields when displaying difference to " + "defaults (--show-diff)", +) +def read( + port: str, + usb: str, + buspal: str, + lpcusbsio: str, + timeout: int, + device: str, + revision: str, + output: str, + yaml_output: str, + show_diff: bool, + show_calc: bool, +) -> None: + """Read IFR page from the device.""" + ifr_obj = ROMCFG(device=device, revision=revision) + ifr_page_address = ifr_obj.config.get_address(device) + ifr_page_length = ifr_obj.BINARY_SIZE + ifr_page_name = ifr_obj.__class__.__name__ + + click.echo(f"{ifr_page_name} page address on {device} is {ifr_page_address:#x}") + + interface = get_interface( + module="mboot", port=port, usb=usb, buspal=buspal, lpcusbsio=lpcusbsio, timeout=timeout + ) + assert isinstance(interface, MBootInterface) + with McuBoot(device=interface) as mboot: + data = mboot.read_memory(address=ifr_page_address, length=ifr_page_length) + if not data: + raise SPSDKError(f"Unable to read data from address {ifr_page_address:#x}") + + if output: + write_file(data, output, "wb") + click.echo(f"{ifr_page_name} data stored to {output}") + if yaml_output: + yaml_data = _parse_binary_data( + data=data, + device=device, + revision=revision, + show_calc=show_calc, + show_diff=show_diff, + ) + write_file(yaml_data, yaml_output) + click.echo(f"Parsed config stored to {yaml_output}") + if not output and not yaml_output: + click.echo(format_raw_data(data=data, use_hexdump=True)) @catch_spsdk_error diff --git a/spsdk/apps/nxpcertgen.py b/spsdk/apps/nxpcertgen.py index f2fb4f54..7f336443 100644 --- a/spsdk/apps/nxpcertgen.py +++ b/spsdk/apps/nxpcertgen.py @@ -75,7 +75,7 @@ def main(log_level: int) -> None: @click.option( "-o", "--output", - type=click.Path(), + type=click.Path(resolve_path=True), required=True, help="Path where certificate will be stored.", ) @@ -123,11 +123,11 @@ def generate(config: str, output: str, encoding: str, force: bool) -> None: encoding_type = Encoding.PEM if encoding.lower() == "pem" else Encoding.DER save_crypto_item(certificate, output, encoding_type=encoding_type) logger.info("Certificate generated successfully...") - click.echo(f"The certificate file has been created: {os.path.abspath(output)}") + click.echo(f"The certificate file has been created: {output}") @main.command(name="get-template", no_args_is_help=True) -@click.argument("output", metavar="PATH", type=click.Path()) +@click.argument("output", metavar="PATH", type=click.Path(resolve_path=True)) @click.option( "-f", "--force", @@ -144,9 +144,9 @@ def get_template(output: str, force: bool) -> None: logger.info("Creating Certificate template...") check_file_exists(output, force) - write_file(load_text(os.path.join(NXPCERTGEN_DATA_FOLDER, "certgen_config.yml")), output) + write_file(load_text(os.path.join(NXPCERTGEN_DATA_FOLDER, "certgen_config.yaml")), output) - click.echo(f"The configuration template file has been created: {os.path.abspath(output)}") + click.echo(f"The configuration template file has been created: {output}") @main.command(name="verify", no_args_is_help=True) diff --git a/spsdk/apps/nxpcrypto.py b/spsdk/apps/nxpcrypto.py index aae3cf07..9c6bf245 100644 --- a/spsdk/apps/nxpcrypto.py +++ b/spsdk/apps/nxpcrypto.py @@ -26,7 +26,7 @@ spsdk_apps_common_options, ) from spsdk.apps.utils.utils import SPSDKAppError, catch_spsdk_error -from spsdk.utils.misc import load_binary +from spsdk.utils.misc import load_binary, write_file @click.group(name="nxpcrypto", no_args_is_help=True, cls=CommandsTreeGroup) @@ -166,8 +166,7 @@ def convert(output_format: str, infile: str, outfile: str, puk: bool, use_pkcs8: y = key.pointQ.y.to_bytes(key_size) # type: ignore # this `to_bytes` doesn't have byteorder out_data = x + y - with open(outfile, "wb") as f: - f.write(out_data) + write_file(out_data, outfile, mode="wb") @key_group.command(name="verify", no_args_is_help=True) diff --git a/spsdk/apps/nxpdebugmbox.py b/spsdk/apps/nxpdebugmbox.py index 4ac04756..0cbe43b9 100644 --- a/spsdk/apps/nxpdebugmbox.py +++ b/spsdk/apps/nxpdebugmbox.py @@ -40,9 +40,15 @@ from spsdk.dat.debug_mailbox import DebugMailbox from spsdk.debuggers.utils import PROBES, open_debug_probe, test_ahb_access from spsdk.exceptions import SPSDKValueError -from spsdk.utils.crypto.rkht import RKHT from spsdk.utils.images import BinaryImage -from spsdk.utils.misc import find_file, load_binary, load_configuration, load_text, write_file +from spsdk.utils.misc import ( + find_file, + import_source, + load_binary, + load_configuration, + load_text, + write_file, +) logger = logging.getLogger(__name__) NXPDEBUGMBOX_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "nxpdebugmbox") @@ -125,7 +131,7 @@ def _open_debugmbox( """Method opens DebugMailbox object based on input arguments. :param debug_probe_params: DebugProbeParams object holding information about parameters for debug probe. - :param debug_mailbox_params: DebugMailboxParams object holding information about parameters for debugmailbox. + :param debug_mailbox_params: DebugMailboxParams object holding information about parameters for debug mailbox. :return: Active DebugMailbox object. :raises SPSDKError: Raised with any kind of problems with debug probe. """ @@ -143,7 +149,7 @@ def _open_debugmbox( try: yield dm except SPSDKError as exc: - raise exc + raise SPSDKError("Problem with debug probe occurred") from exc finally: dm.close() @@ -153,7 +159,7 @@ def _open_debugmbox( "-i", "--interface", type=click.Choice(list(PROBES.keys())), - help="Probe interface selection,if not specified, all available debug probe interfaces are used.", + help="Probe interface selection, if not specified, all available debug probe interfaces are used.", ) @click.option( "-s", @@ -175,7 +181,7 @@ def _open_debugmbox( "--timing", type=float, default=0.0, - help="Time of extra delay after reset sequence, defaults to 1.0 second", + help="Duration of additional delay after reset sequence, defaults to 0 seconds", ) @click.option( "-n", @@ -274,7 +280,7 @@ def auth( """Perform the Debug Authentication. :param debug_probe_params: DebugProbeParams object holding information about parameters for debug probe. - :param debug_mailbox_params: DebugMailboxParams object holding information about parameters for debugmailbox. + :param debug_mailbox_params: DebugMailboxParams object holding information about parameters for debug mailbox. :param protocol: Debug authentication protocol. :param beacon: Authentication beacon. :param certificate: Path to Debug Credentials. @@ -297,7 +303,6 @@ def auth( dac.validate_against_dc(debug_cred) dar = DebugAuthenticateResponse.create( version=protocol.version, - socc=dac.socc, dc=debug_cred, auth_beacon=beacon, dac=dac, @@ -698,7 +703,7 @@ def read_memory_command( data = read_memory(pass_obj["debug_probe_params"], address, byte_count, progress_callback) if out_file: write_file(data, out_file, mode="wb") - click.echo(f"The memory has been read and write into {out_file}") + click.echo(f"The memory has been read and written into {out_file}") else: click.echo(format_raw_data(data, use_hexdump=use_hexdump)) @@ -762,8 +767,8 @@ def write_memory_command(pass_obj: dict, address: int, data_source: str) -> None ADDRESS - starting address FILE - write the content of this file BYTE_COUNT - if specified, load only first BYTE_COUNT number of bytes from file - HEX-DATA - string of hex values: {{112233}}, {{11 22 33}} - - when using Jupyter notebook, use [[ ]] instead of {{ }}: eg. [[11 22 33]] + HEX-DATA - string of hex values: {{112233}}, "{{11 22 33}}" + - when using Jupyter notebook, use [[ ]] instead of {{ }}: eg. [[112233]] """ try: data = parse_hex_data(data_source) @@ -772,7 +777,7 @@ def write_memory_command(pass_obj: dict, address: int, data_source: str) -> None with open(file_path, "rb") as f: data = f.read(size) write_memory(pass_obj["debug_probe_params"], address, data) - click.echo("The memory has been write successfully.") + click.echo("The memory has been written successfully.") def write_memory(debug_probe_params: DebugProbeParams, address: int, data: bytes) -> None: @@ -819,6 +824,75 @@ def write_memory(debug_probe_params: DebugProbeParams, address: int, data: bytes ) +@main.command(name="get-uuid") +@click.pass_obj +def get_uuid_command(pass_obj: dict) -> None: + """Get the UUID from target if possible. + + Some devices need to call 'start' command prior the get-uuid! + Also there could be issue with repeating of this command without hard reset of device 'reset -h'. + """ + uuid = get_uuid(pass_obj["debug_probe_params"], pass_obj["debug_mailbox_params"]) + if uuid: + click.echo(f"The device UUID is: {uuid.hex()}") + else: + click.echo(f"The device UUID is not possible to retrieve from target.") + + +def get_uuid( + debug_probe_params: DebugProbeParams, debug_mailbox_params: DebugMailboxParams +) -> Optional[bytes]: + """Get the UUID from target if possible. + + Some devices need to call 'start' command prior the get-uuid! + Also there could be issue with repeating of this command without hard reset of device 'reset -h'. + + :param debug_probe_params: DebugProbeParams object holding information about parameters for debug probe. + :param debug_mailbox_params: DebugMailboxParams object holding information about parameters for debug mailbox. + :raises SPSDKAppError: Raised if any error occurred. + :return: UUID value in bytes if succeeded, None otherwise. + """ + try: + with open_debug_probe( + debug_probe_params.interface, + debug_probe_params.serial_no, + debug_probe_params.debug_probe_user_params, + ) as debug_probe: + try: + dm = DebugMailbox( + debug_probe=debug_probe, + reset=debug_mailbox_params.reset, + moredelay=debug_mailbox_params.more_delay, + op_timeout=debug_mailbox_params.operation_timeout, + ) + dac_data = dm_commands.DebugAuthenticationStart(dm=dm, resplen=26).run() + except SPSDKError: + debug_probe.close() + debug_probe.open() + dm = DebugMailbox( + debug_probe=debug_probe, + reset=debug_mailbox_params.reset, + moredelay=debug_mailbox_params.more_delay, + op_timeout=debug_mailbox_params.operation_timeout, + ) + dac_data = dm_commands.DebugAuthenticationStart(dm=dm, resplen=30).run() + # convert List[int] to bytes + dac_data_bytes = struct.pack(f"<{len(dac_data)}I", *dac_data) + dac = DebugAuthenticationChallenge.parse(dac_data_bytes) + except Exception as e: + raise SPSDKAppError(f"Getting UUID from target failed: {e}") from e + + if dac.uuid == bytes(16): + logger.warning(f"The valid UUID is not included in DAC.") + logger.info(f"DAC info:\n {dac.info()}") + return None + + logger.info( + f"Got DAC from SOCC:'{DebugCredential.get_socc_description(dac.version, dac.socc)}' to retrieve UUID." + ) + return dac.uuid + + @main.command(name="gendc", no_args_is_help=True) @click.option( "-c", @@ -883,7 +957,7 @@ def gendc( """Generate debug certificate (DC). :param protocol: Debug authentication protocol. - :param plugin: External python file containing a custom SignatureProvider implementation. + :param plugin: Path to external python file containing a custom SignatureProvider implementation. :param dc_file_path: Path to debug certificate file. :param config: YAML credential config file. :param elf2sb_config: Root Of Trust from configuration file used by elf2sb tool. @@ -892,20 +966,8 @@ def gendc( """ try: if plugin: - # if a plugin is present simply load it - # The SignatureProvider will automatically pick up any implementation(s) - from importlib.util import ( # pylint: disable=import-outside-toplevel - module_from_spec, - spec_from_file_location, - ) - - spec = spec_from_file_location(name="plugin", location=plugin) # type: ignore - assert spec - mod = module_from_spec(spec) - spec.loader.exec_module(mod) # type: ignore - + import_source(plugin) check_file_exists(dc_file_path, force) - logger.info("Loading configuration from yml file...") yaml_content = load_configuration(config) if elf2sb_config: @@ -967,7 +1029,7 @@ def get_template(output: str, force: bool) -> None: :param force: Force overwriting of an existing file. """ check_file_exists(str(output), force) - write_file(load_text(os.path.join(NXPDEBUGMBOX_DATA_FOLDER, "template_config.yml")), output) + write_file(load_text(os.path.join(NXPDEBUGMBOX_DATA_FOLDER, "template_config.yaml")), output) @catch_spsdk_error diff --git a/spsdk/apps/nxpdevhsm.py b/spsdk/apps/nxpdevhsm.py index 0bffb146..cffce79a 100644 --- a/spsdk/apps/nxpdevhsm.py +++ b/spsdk/apps/nxpdevhsm.py @@ -10,29 +10,39 @@ import logging import os import sys -from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Optional, Tuple import click from spsdk import SPSDK_DATA_FOLDER, SPSDKError, SPSDKValueError -from spsdk import __version__ as version from spsdk.apps.utils.common_cli_options import ( CommandsTreeGroup, isp_interfaces, spsdk_apps_common_options, ) from spsdk.apps.utils.utils import catch_spsdk_error, format_raw_data, get_interface -from spsdk.image import MBIMG_SCH_FILE, SB3_SCH_FILE +from spsdk.image import MBIMG_SCH_FILE from spsdk.mboot.commands import TrustProvKeyType, TrustProvOemKeyType from spsdk.mboot.interfaces import MBootInterface from spsdk.mboot.mcuboot import McuBoot from spsdk.sbfile.sb31.commands import CmdLoadKeyBlob -from spsdk.sbfile.sb31.images import SecureBinary31Commands, SecureBinary31Header +from spsdk.sbfile.sb31.images import ( + SB3_SCH_FILE, + SecureBinary31, + SecureBinary31Commands, + SecureBinary31Header, +) from spsdk.utils.crypto.cert_blocks import CertificateBlockHeader from spsdk.utils.crypto.common import crypto_backend from spsdk.utils.database import Database -from spsdk.utils.misc import load_configuration, value_to_bool, value_to_int -from spsdk.utils.schema_validator import ValidationSchemas, check_config +from spsdk.utils.misc import ( + load_binary, + load_configuration, + value_to_bool, + value_to_int, + write_file, +) +from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas, check_config logger = logging.getLogger(__name__) @@ -57,8 +67,8 @@ class DeviceHsm: DEVBUFF_GEN_MASTER_CUST_CERT_PUK_OUTPUT_SIZE = 64 DEVBUFF_HSM_GENKEY_KEYBLOB_SIZE = 48 DEVBUFF_HSM_GENKEY_KEYBLOB_PUK_SIZE = 64 - DEVBUFF_USER_PCK_KEY_SIZE = 32 - DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE = 48 + DEVBUFF_CUST_MK_SK_KEY_SIZE = 32 + DEVBUFF_WRAPPED_CUST_MK_SK_KEY_SIZE = 48 DEVBUFF_DATA_BLOCK_SIZE = 256 DEVBUFF_SB3_SIGNATURE_SIZE = 64 @@ -67,7 +77,7 @@ class DeviceHsm: def __init__( self, mboot: McuBoot, - user_pck: bytes, + cust_mk_sk: bytes, oem_share_input: bytes, info_print: Callable, family: str, @@ -80,7 +90,7 @@ def __init__( :param mboot: mBoot communication interface. :param oem_share_input: OEM share input data. - :param user_pck: USER PCK key. + :param cust_mk_sk: Customer Master Key Symmetric Key. :param family: chip family :param container_conf: Optional elftosb configuration file (to specify user list of SB commands). :param workspace: Optional folder to store middle results. @@ -90,7 +100,7 @@ def __init__( """ self.database = Database(NXPDEVHSM_DATABASE_FILE) self.mboot = mboot - self.user_pck = user_pck + self.cust_mk_sk = cust_mk_sk self.oem_share_input = oem_share_input self.info_print = info_print self.workspace = workspace @@ -115,7 +125,7 @@ def __init__( # validate input configuration check_config( config_data, - DeviceHsm.get_validation_schemas(), + DeviceHsm.get_validation_schemas(family, include_test_configuration=True), search_paths=[os.path.dirname(container_conf)], ) self.config_data = config_data @@ -124,7 +134,7 @@ def __init__( if "timestamp" in config_data: self.timestamp = value_to_int(str(config_data.get("timestamp"))) - self.wrapped_user_pck = bytes() + self.wrapped_cust_mk_sk = bytes() self.final_sb = bytes() self.family = family @@ -136,18 +146,64 @@ def command_order(self) -> bool: """Update command order based on family.""" return value_to_bool(self.database.get_device_value("order", self.family)) + @staticmethod + def get_supported_families() -> List[str]: + """Get the list of supported families by Device HSM. + + :return: List of supported families. + """ + return Database(NXPDEVHSM_DATABASE_FILE).devices.device_names + @classmethod - def get_validation_schemas(cls) -> List[Dict[str, Any]]: + def get_validation_schemas( + cls, family: str, include_test_configuration: bool = False + ) -> List[Dict[str, Any]]: """Create the list of validation schemas. + :param family: Family description. + :param include_test_configuration: Add also testing configuration schemas. :return: List of validation schemas. """ mbi_sch_cfg = ValidationSchemas().get_schema_file(MBIMG_SCH_FILE) sb3_sch_cfg = ValidationSchemas().get_schema_file(SB3_SCH_FILE) - ret: List[Dict[str, Any]] = [] - ret.extend([mbi_sch_cfg["firmware_version"]]) - ret.extend([sb3_sch_cfg[x] for x in ["sb3_description", "sb3_commands", "sb3_test"]]) + schemas: List[Dict[str, Any]] = [] + schemas.append(mbi_sch_cfg["firmware_version"]) + schemas.append(sb3_sch_cfg["sb3_family"]) + schemas.append(sb3_sch_cfg["sb3_description"]) + schemas.extend(SecureBinary31.get_commands_validation_schemas(family)) + if include_test_configuration: + schemas.append(sb3_sch_cfg["sb3_test"]) + # find family + for schema in schemas: + if "properties" in schema and "family" in schema["properties"]: + schema["properties"]["family"]["enum"] = cls.get_supported_families() + break + return schemas + + @classmethod + def generate_config_template(cls, family: str) -> Dict[str, str]: + """Generate configuration for selected family. + + :param family: Family description. + :return: Dictionary of individual templates (key is name of template, value is template itself). + """ + ret: Dict[str, str] = {} + + if family in cls.get_supported_families(): + schemas = cls.get_validation_schemas(family) + schemas.append(ValidationSchemas.get_schema_file(SB3_SCH_FILE)["sb3_output"]) + override = {} + override["family"] = family + + yaml_data = ConfigTemplate( + f"DEVHSM procedure Secure Binary v3.1 Configuration template for {family}.", + schemas, + override, + ).export_to_yaml() + + ret[f"sb31_{family}_devhsm"] = yaml_data + return ret def create_sb3(self) -> None: @@ -173,11 +229,11 @@ def create_sb3(self) -> None: self.info_print(" 4: Generating 48 bytes FW encryption keys.") cust_fw_enc_prk, _ = self.generate_key(TrustProvOemKeyType.MFWENCK, "CUST_FW_ENC_SK") - # 5: Call hsm_store_key to generate user defined CUST_MK_SK (aka PCK). + # 5: Call hsm_store_key to generate user defined CUST_MK_SK. # Will be stored into PFR using loadKeyBlob SB3 command. # Use NXP_CUST_KEK_EXT_SK in SB json - self.info_print(" 5: Wrapping user PCK key.") - self.wrapped_user_pck = self.wrap_key(self.user_pck) + self.info_print(" 5: Wrapping CUST_MK_SK key.") + self.wrapped_cust_mk_sk = self.wrap_key(self.cust_mk_sk) # 6: Generate template sb3 fw, sb3ImageType=6 self.info_print(" 6: Creating template un-encrypted SB3 header and data blobs.") @@ -213,7 +269,7 @@ def create_sb3(self) -> None: index=key_blob_command_position, command=CmdLoadKeyBlob( offset=self.update_keyblob_offset(), - data=self.wrapped_user_pck, + data=self.wrapped_cust_mk_sk, key_wrap_id=CmdLoadKeyBlob.get_key_id( family=self.family, key_name=CmdLoadKeyBlob.KeyTypes.NXP_CUST_KEK_EXT_SK ), @@ -419,23 +475,23 @@ def generate_key( return prk, puk - def wrap_key(self, user_pck: bytes) -> bytes: - """Wrap the user PCK key. + def wrap_key(self, cust_mk_sk: bytes) -> bytes: + """Wrap the CUST_MK_SK key. - :param user_pck: User PCK key + :param cust_mk_sk : Customer Master Key Symmetric Key :raises SPSDKError: In case of any vulnerability. - :return: Wrapped user PCK by RFC3396. + :return: Wrapped CUST_MK_SK by RFC3396. """ - if not self.mboot.write_memory(self.DEVBUFF_BASE0, user_pck): + if not self.mboot.write_memory(self.DEVBUFF_BASE0, cust_mk_sk): raise SPSDKError( - f"Cannot write USER_PCK into device. Error: {self.mboot.status_string}" + f"Cannot write CUST_MK_SK into device. Error: {self.mboot.status_string}" ) hsm_store_key_res = self.mboot.tp_hsm_store_key( TrustProvKeyType.CKDFK, 0x01, self.DEVBUFF_BASE0, - self.DEVBUFF_USER_PCK_KEY_SIZE, + self.DEVBUFF_CUST_MK_SK_KEY_SIZE, self.DEVBUFF_BASE1, self.DEVBUFF_SIZE, ) @@ -443,21 +499,22 @@ def wrap_key(self, user_pck: bytes) -> bytes: if not hsm_store_key_res: raise SPSDKError(f"HSM Store Key command failed. Error: {self.mboot.status_string}") - if hsm_store_key_res[1] != self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE: + if hsm_store_key_res[1] != self.DEVBUFF_WRAPPED_CUST_MK_SK_KEY_SIZE: raise SPSDKError("HSM Store Key command has invalid results.") - wrapped_user_pck = self.mboot.read_memory( + wrapped_cust_mk_sk = self.mboot.read_memory( self.DEVBUFF_BASE1, - self.DEVBUFF_WRAPPED_USER_PCK_KEY_SIZE, + self.DEVBUFF_WRAPPED_CUST_MK_SK_KEY_SIZE, ) - if not wrapped_user_pck: + + if not wrapped_cust_mk_sk: raise SPSDKError( - f"Cannot read WRAPPED USER PCK from device. Error: {self.mboot.status_string}" + f"Cannot read WRAPPED CUST_MK_SK from device. Error: {self.mboot.status_string}" ) - self.store_temp_res("CUST_MK_SK.bin", wrapped_user_pck) + self.store_temp_res("CUST_MK_SK.bin", wrapped_cust_mk_sk) - return wrapped_user_pck + return wrapped_cust_mk_sk def sign_data_blob(self, data_to_sign: bytes, key: bytes) -> bytes: """Get HSM encryption sign for data blob. @@ -517,9 +574,7 @@ def store_temp_res(self, file_name: str, data: bytes, group: Optional[str] = Non os.mkdir(group_dir) filename = os.path.join(self.workspace, group or "", file_name) - - with open(filename, "wb") as data_file: - data_file.write(data) + write_file(data, filename, mode="wb") def get_cmd_from_config(self) -> List[Dict[str, Any]]: """Process command description into a command object. @@ -605,24 +660,24 @@ def encrypt_data_blocks( return encrypted_blocks -def get_user_pck(key: BinaryIO) -> bytes: +def get_cust_mk_sk(key: str) -> bytes: """Get binary from text or binary file. - :param key: Binary user PCK key file. + :param key: Binary customer master key symmetric key file. :return: Binary array loaded from file. :raises SPSDKValueError: When invalid input value is recognized. """ - user_pck = key.read() + cust_mk_sk = load_binary(key) - if len(user_pck) != 32: + if len(cust_mk_sk) != 32: raise SPSDKValueError( - f"Invalid length of USER PCK INPUT ({len(user_pck)} not equal to 32)." + f"Invalid length of CUST_MK_SK INPUT ({len(cust_mk_sk )} not equal to 32)." ) - return user_pck + return cust_mk_sk -def get_oem_share_input(binary: BinaryIO) -> bytes: +def get_oem_share_input(binary: str) -> bytes: """Get binary from text or binary file. :param binary: Path to binary file. @@ -630,7 +685,7 @@ def get_oem_share_input(binary: BinaryIO) -> bytes: :raises SPSDKValueError: When invalid input value is recognized. """ if binary: - oem_share_input = binary.read() + oem_share_input = load_binary(binary) else: oem_share_input = crypto_backend().random_bytes(16) @@ -655,15 +710,16 @@ def main(log_level: int) -> int: @click.option( "-k", "--key", - type=click.File(mode="rb"), + type=click.Path(exists=True, file_okay=True, dir_okay=False), required=True, - help="User PCK secret file (32-bytes long binary file). PCK (provisioned by OEM, known by OEM) - Part Common Key." - " This is a 256-bit pre-shared AES key provisioned by OEM. PCK is used to derive FW image encryption keys.", + help="""Customer Master Key Symmetric Key secret file (32-bytes long binary file). + CUST_MK_SK (provisioned by OEM, known by OEM). + This is a 256-bit pre-shared AES key provisioned by OEM. CUST_MK_SK is used to derive FW image encryption keys.""", ) @click.option( "-o", "--oem-share-input", - type=click.File(mode="rb"), + type=click.Path(exists=True, file_okay=True, dir_okay=False), help="OEM share input file to use as a seed to randomize the provisioning process (16-bytes long binary file).", ) @click.option( @@ -684,9 +740,9 @@ def main(log_level: int) -> int: @click.option( "-f", "--family", - required=False, + required=True, help="Select the chip family.", - type=click.Choice(["lpc55s3x"], case_sensitive=False), + type=click.Choice(DeviceHsm.get_supported_families(), case_sensitive=False), ) @click.option( "-ir/-IR", @@ -707,15 +763,15 @@ def main(log_level: int) -> int: "By default this reset is ENABLED." ), ) -@click.argument("output-path", type=click.File(mode="wb")) +@click.argument("output-path", type=click.Path()) def generate( port: str, usb: str, buspal: str, lpcusbsio: str, - oem_share_input: BinaryIO, - key: BinaryIO, - output_path: BinaryIO, + oem_share_input: str, + key: str, + output_path: str, workspace: str, container_conf: str, timeout: int, @@ -734,7 +790,7 @@ def generate( assert isinstance(interface, MBootInterface) oem_share_in = get_oem_share_input(oem_share_input) - user_pck = get_user_pck(key) + cust_mk_sk = get_cust_mk_sk(key) if container_conf: family_from_cfg = load_configuration(container_conf).get("family") if not isinstance(family_from_cfg, str): @@ -744,28 +800,56 @@ def generate( f"Family from json configuration file: {family_from_cfg} differs from the family parameter {family}" ) - if not family and not container_conf: - raise SPSDKError( - "Need to provide family. Either as -f parameter or in json configuration file." - ) - family_final = family if not container_conf else family_from_cfg - assert isinstance(family_final, str) with McuBoot(interface) as mboot: devhsm = DeviceHsm( mboot=mboot, - user_pck=user_pck, + cust_mk_sk=cust_mk_sk, oem_share_input=oem_share_in, info_print=click.echo, container_conf=container_conf, workspace=workspace, - family=family_final, + family=family, initial_reset=initial_reset, final_reset=final_reset, ) devhsm.create_sb3() - output_path.write(devhsm.export()) + write_file(devhsm.export(), output_path, "wb") + + click.echo(f"Final SB3 file has been written: {os.path.abspath(output_path)}") + + +@main.command(name="get-template", no_args_is_help=True) +@click.option( + "-f", + "--family", + required=True, + help="Select the chip family.", + type=click.Choice(DeviceHsm.get_supported_families(), case_sensitive=False), +) +@click.option( + "-o", + "--overwrite", + default=False, + type=bool, + is_flag=True, + help="Allow overwriting existing template file.", +) +@click.argument("output", type=click.Path(resolve_path=True)) +def get_template_command(family: str, overwrite: bool, output: str) -> None: + """Create template of configuration in YAML format. - click.echo(f"Final SB3 file has been written: {os.path.abspath(output_path.name)}") + The template file name is specified as argument of this command. + """ + get_template(family, overwrite, output) + + +def get_template(family: str, overwrite: bool, output: str) -> None: + """Create template of configuration in YAML format.""" + if not os.path.isfile(output) or overwrite: + click.echo(f"Creating {output} template file.") + write_file(DeviceHsm.generate_config_template(family)[f"sb31_{family}_devhsm"], output) + else: + click.echo(f"Skip creating {output}, this file already exists.") @catch_spsdk_error diff --git a/spsdk/apps/nxpimage.py b/spsdk/apps/nxpimage.py index 4a7ad487..3bd5f7eb 100644 --- a/spsdk/apps/nxpimage.py +++ b/spsdk/apps/nxpimage.py @@ -17,19 +17,21 @@ from spsdk.apps.utils.common_cli_options import CommandsTreeGroup, spsdk_apps_common_options from spsdk.apps.utils.utils import INT, SPSDKAppError, catch_spsdk_error, get_key, store_key from spsdk.exceptions import SPSDKError -from spsdk.image import SB3_SCH_FILE, TrustZone, get_mbi_class +from spsdk.image import TrustZone, get_mbi_class from spsdk.image.ahab.ahab_container import AHABImage from spsdk.image.ahab.signed_msg import SignedMessage from spsdk.image.bee import BeeNxp from spsdk.image.bootable_image.bimg import BootableImage, get_bimg_class from spsdk.image.fcb.fcb import FCB +from spsdk.image.hab.hab_container import HabContainer from spsdk.image.keystore import KeyStore from spsdk.image.mbimg import mbi_generate_config_templates, mbi_get_supported_families from spsdk.image.xmcd.xmcd import XMCD +from spsdk.sbfile.sb2 import sly_bd_parser as bd_parser from spsdk.sbfile.sb2.commands import CmdLoad from spsdk.sbfile.sb2.images import BootImageV21, generate_SB21 -from spsdk.sbfile.sb31.images import SecureBinary31 -from spsdk.utils.crypto.cert_blocks import CertBlockV2 +from spsdk.sbfile.sb31.images import SB3_SCH_FILE, SecureBinary31 +from spsdk.utils.crypto.cert_blocks import CertBlockV2, CertBlockV31, convert_to_ecc_key from spsdk.utils.crypto.iee import IeeNxp from spsdk.utils.crypto.otfad import OtfadNxp from spsdk.utils.images import BinaryImage, BinaryPattern @@ -37,6 +39,7 @@ align, align_block, get_abs_path, + import_source, load_binary, load_configuration, load_text, @@ -65,20 +68,34 @@ def mbi_group() -> None: @mbi_group.command(name="export", no_args_is_help=True) +@click.option( + "--plugin", + required=False, + help="External python file/package containing a custom SignatureProvider implementation.", +) @click.argument("config", type=click.Path(exists=True, readable=True)) -def mbi_export_command(config: str) -> None: +def mbi_export_command( + config: str, + plugin: str, +) -> None: """Generate Master Boot Image from YAML/JSON configuration. CONFIG is path to the YAML/JSON configuration The configuration template files could be generated by subcommand 'get-templates'. """ - mbi_export(config) + mbi_export(config, plugin) -def mbi_export(config: str) -> None: - """Generate Master Boot Image from YAML/JSON configuration.""" +def mbi_export(config: str, plugin: Optional[str] = None) -> None: + """Generate Master Boot Image from YAML/JSON configuration. + + :param config: Path to the YAML/JSON configuration + :param plugin: Path to external python file containing a custom SignatureProvider implementation. + """ config_data = load_configuration(config) + if plugin: + import_source(plugin) config_dir = os.path.dirname(config) mbi_cls = get_mbi_class(config_data) check_config(config_data, mbi_cls.get_validation_schemas(), search_paths=[config_dir]) @@ -122,7 +139,7 @@ def mbi_get_templates(chip_family: str, overwrite: bool, output: str) -> None: """Create template of MBI configurations in YAML format.""" templates = mbi_generate_config_templates(chip_family) for file_name, template in templates.items(): - full_file_name = os.path.join(output, file_name + ".yml") + full_file_name = os.path.abspath(os.path.join(output, file_name + ".yaml")) if not os.path.isfile(full_file_name) or overwrite: click.echo(f"Creating {full_file_name} template file.") write_file(template, full_file_name) @@ -142,7 +159,7 @@ def sb21_group() -> None: type=click.Path(exists=True), help="BD configuration file to produce secure binary v2.x", ) -@click.option("-o", "--output", type=click.Path(), help="Output file path.") +@click.option("-o", "--output", type=click.Path(resolve_path=True), help="Output file path.") @click.option( "-k", "--key", type=click.Path(exists=True), help="Add a key file and enable encryption." ) @@ -224,7 +241,7 @@ def sb21_export( @click.option( "-b", "--binary", - type=click.Path(exists=True, readable=True), + type=click.Path(exists=True, readable=True, resolve_path=True), required=True, help="Path to the SB2 container that would be parsed.", ) @@ -253,16 +270,22 @@ def sb21_parse(binary: str, key: str, parsed_data: str) -> None: if isinstance(parsed_sb.cert_block, CertBlockV2): for cert_idx, certificate in enumerate(parsed_sb.cert_block.certificates): - file_name = f"certificate_{cert_idx}_der.cer" + file_name = os.path.abspath( + os.path.join(parsed_data, f"certificate_{cert_idx}_der.cer") + ) logger.debug(f"Dumping certificate {file_name}") - write_file(certificate.dump(), os.path.join(parsed_data, file_name), mode="wb") + write_file(certificate.dump(), file_name, mode="wb") for section_idx, boot_sections in enumerate(parsed_sb._boot_sections): for command_idx, command in enumerate(boot_sections._commands): if isinstance(command, CmdLoad): - file_name = f"section_{section_idx}_load_command_{command_idx}_data.bin" + file_name = os.path.abspath( + os.path.join( + parsed_data, f"section_{section_idx}_load_command_{command_idx}_data.bin" + ) + ) logger.debug(f"Dumping load command data {file_name}") - write_file(command.data, os.path.join(parsed_data, file_name), mode="wb") + write_file(command.data, file_name, mode="wb") logger.debug(parsed_sb.info()) write_file( @@ -285,7 +308,7 @@ def sb21_parse(binary: str, key: str, parsed_data: str) -> None: @click.option( "-o", "--output-folder", - type=click.Path(), + type=click.Path(resolve_path=True), help="Output folder where the sbkek.txt and sbkek.bin will be stored", ) def get_sbkek_command(master_key: str, output_folder: str) -> None: @@ -344,12 +367,15 @@ def sb31_export(config: str) -> None: """Generate Secure Binary v3.1 Image from YAML/JSON configuration.""" config_data = load_configuration(config) config_dir = os.path.dirname(config) - schemas = SecureBinary31.get_validation_schemas(include_test_configuration=True) + check_config(config_data, SecureBinary31.get_validation_schemas_family()) + schemas = SecureBinary31.get_validation_schemas( + config_data["family"], include_test_configuration=True + ) schemas.append(ValidationSchemas.get_schema_file(SB3_SCH_FILE)["sb3_output"]) check_config(config_data, schemas, search_paths=[config_dir]) sb3 = SecureBinary31.load_from_config(config_data, search_paths=[config_dir]) - sb3_data = sb3.export() + sb3_data = sb3.export() sb3_output_file_path = get_abs_path(config_data["containerOutputFile"], config_dir) write_file(sb3_data, sb3_output_file_path, mode="wb") @@ -373,7 +399,7 @@ def sb31_export(config: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def sb31_get_template_command(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -393,6 +419,94 @@ def sb31_get_template(chip_family: str, overwrite: bool, output: str) -> None: click.echo(f"Skip creating {output}, this file already exists.") +@main.group(name="cert-block", no_args_is_help=True) +def cert_block_group() -> None: # pylint: disable=unused-argument + """Group of sub-commands related to certification block.""" + + +@cert_block_group.command(name="get-template", no_args_is_help=True) +@click.option( + "-o", + "--overwrite", + default=False, + type=bool, + is_flag=True, + help="Allow overwriting existing template file.", +) +@click.argument("output", type=click.Path(resolve_path=True)) +def cert_block_get_template_command(overwrite: bool, output: str) -> None: + """Create template of configuration in YAML format. + + The template file name is specified as argument of this command. + """ + cert_block_get_template(overwrite, output) + + +def cert_block_get_template(overwrite: bool, output: str) -> None: + """Create template of configuration in YAML format.""" + if not os.path.isfile(output) or overwrite: + click.echo(f"Creating {output} template file.") + write_file(CertBlockV31.generate_config_template(), output) + else: + click.echo(f"Skip creating {output}, this file already exists.") + + +@cert_block_group.command(name="export", no_args_is_help=True) +@click.argument("config", type=click.Path(exists=True, readable=True)) +def cert_block_export_command(config: str) -> None: + """Generate Certificate Block v3.1 from YAML/JSON configuration. + + CONFIG is path to the YAML/JSON configuration. + RoTKTH is printed out in verbose mode. + + The configuration template files could be generated by subcommand 'get-template'. + """ + cert_block_export(config) + + +def cert_block_export(config: str) -> None: + """Generate Certificate Block v3.1 from YAML/JSON configuration.""" + config_data = load_configuration(config) + config_dir = os.path.dirname(config) + schemas = CertBlockV31.get_validation_schemas() + check_config(config_data, schemas, search_paths=[config_dir]) + cert_block = CertBlockV31.from_config(config_data, search_paths=[config_dir]) + cert_data = cert_block.export() + + cert_block_output_file_path = get_abs_path(config_data["containerOutputFile"], config_dir) + write_file(cert_data, cert_block_output_file_path, mode="wb") + + click.echo(f"Success. (Certificate Block 3.1: {cert_block_output_file_path} created.)") + + +@cert_block_group.command(name="parse", no_args_is_help=True) +@click.option( + "-b", + "--binary", + type=click.Path(exists=True, readable=True, resolve_path=True), + required=True, + help="Path to binary Certificate v3.1 image to parse.", +) +@click.argument("parsed-data", type=click.Path(resolve_path=True)) +def cert_block_parse_command(binary: str, parsed_data: str) -> None: + """Parse Certificate Block v3.1. + + PARSED-DATA is path where the parsed certification block will be stored. + RoTKTH is printed out in verbose mode. + """ + cert_block_parse(binary, parsed_data) + + +def cert_block_parse(binary: str, parsed_data: str) -> None: + """Parse Certificate Block v3.1.""" + cert_block = CertBlockV31.parse(load_binary(binary)) + logger.info(cert_block.info()) + write_file( + cert_block.create_config(parsed_data), os.path.join(parsed_data, "cert_block_config.yaml") + ) + click.echo(f"Success. (Certificate Block 3.1: {binary} has been parsed into {parsed_data}.)") + + @main.group(name="tz", no_args_is_help=True) def tz_group() -> None: """Group of sub-commands related to Trust Zone.""" @@ -451,7 +565,7 @@ def tz_export(config: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def tz_get_template_command(chip_family: str, revision: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -520,14 +634,14 @@ def ahab_export(config: str) -> None: write_file(blhost_script, get_abs_path(f"{file_name}_blhost.bcf", ahab_output_dir)) except SPSDKError: pass - click.echo(f"Generated SRK hash files ({file_name}*.*).") + click.echo(f"Generated SRK hash files ({os.path.abspath(file_name)}*.*).") @ahab_group.command(name="parse", no_args_is_help=True) @click.option( "-f", "--chip-family", - default="rt1180", + default="rt118x", type=click.Choice(AHABImage.get_supported_families(), case_sensitive=False), required=True, help="Select the chip family.", @@ -541,7 +655,7 @@ def ahab_export(config: str) -> None: @click.option( "-b", "--binary", - type=click.Path(exists=True, readable=True), + type=click.Path(exists=True, readable=True, resolve_path=True), required=True, help="Path to binary AHAB image to parse.", ) @@ -555,7 +669,7 @@ def ahab_export(config: str) -> None: "It could be specified as binary/HEX text file path or directly HEX string" ), ) -@click.argument("parsed-data", type=click.Path()) +@click.argument("parsed-data", type=click.Path(resolve_path=True)) def ahab_parse_command( chip_family: str, image_type: str, binary: str, dek: str, parsed_data: str ) -> None: @@ -626,7 +740,7 @@ def ahab_parse(chip_family: str, image_type: str, binary: str, dek: str, parsed_ @click.option( "-f", "--chip-family", - default="rt1180", + default="rt118x", type=click.Choice(AHABImage.get_supported_families(), case_sensitive=False), required=True, help="Select the chip family.", @@ -639,7 +753,7 @@ def ahab_parse(chip_family: str, image_type: str, binary: str, dek: str, parsed_ is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def ahab_get_template_command(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -688,23 +802,15 @@ def signed_msg_export(config: str) -> None: @signed_msg.command(name="parse", no_args_is_help=True) -@click.option( - "-f", - "--chip-family", - default="rt1180", - type=click.Choice(AHABImage.get_supported_families(), case_sensitive=False), - required=True, - help="Select the chip family.", -) @click.option( "-b", "--binary", - type=click.Path(exists=True, readable=True), + type=click.Path(exists=True, readable=True, resolve_path=True), required=True, help="Path to binary Signed message image to parse.", ) -@click.argument("parsed-data", type=click.Path()) -def signed_msg_parse(chip_family: str, binary: str, parsed_data: str) -> None: +@click.argument("parsed-data", type=click.Path(resolve_path=True)) +def signed_msg_parse(binary: str, parsed_data: str) -> None: """Parse Signed message Image into YAML configuration and binary images.""" if not os.path.exists(parsed_data): os.makedirs(parsed_data, exist_ok=True) @@ -738,7 +844,7 @@ def signed_msg_parse(chip_family: str, binary: str, parsed_data: str) -> None: @click.option( "-f", "--chip-family", - default="rt1180", + default="rt118x", type=click.Choice(AHABImage.get_supported_families(), case_sensitive=False), required=True, help="Select the chip family.", @@ -751,7 +857,7 @@ def signed_msg_parse(chip_family: str, binary: str, parsed_data: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def signed_msg_get_template(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -785,7 +891,7 @@ def otfad_group() -> None: type=INT(), help=( "OTFAD peripheral index - This is needed to generate proper " - "indexes of fuses in optional BLHOST script. For example for RT1180." + "indexes of fuses in optional BLHOST script. For example for RT118x." ), ) @click.argument("config", type=click.Path(exists=True, readable=True)) @@ -808,12 +914,14 @@ def otfad_export(alignment: int, config: str, index: Optional[int] = None) -> No schemas = OtfadNxp.get_validation_schemas(family) check_config(config_data, schemas, search_paths=[config_dir]) otfad = OtfadNxp.load_from_config(config_data, search_paths=[config_dir]) - binary_image = otfad.binary_image(data_alignment=alignment) + otfad_table_name = config_data.get("keyblob_name", "OTFAD_Table") + binary_image = otfad.binary_image(data_alignment=alignment, otfad_table_name=otfad_table_name) logger.info(f" The OTFAD image structure:\n{binary_image.draw()}") output_folder = get_abs_path(config_data["output_folder"], config_dir) + output_name = config_data.get("output_name", "otfad_whole_image") + ".bin" - otfad_all = os.path.join(output_folder, "otfad_whole_image.bin") + otfad_all = os.path.join(output_folder, output_name) write_file(binary_image.export(), otfad_all, mode="wb") logger.info(f"Created OTFAD Image:\n{otfad_all}") sb21_supported = otfad.database.get_device_value("sb_21_supported", otfad.family, default=False) @@ -905,7 +1013,7 @@ def otfad_export(alignment: int, config: str, index: Optional[int] = None) -> No @click.option( "-o", "--output-folder", - type=click.Path(), + type=click.Path(resolve_path=True), help="Optional result output folder (otfad_kek.bin/txt, optionally BLHOST scripts to load keys into Fuses)", ) def otfad_get_kek_command( @@ -965,7 +1073,7 @@ def otfad_get_kek( is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def otfad_get_template_command(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -1056,7 +1164,7 @@ def iee_export(config: str) -> None: @click.option( "-f", "--chip-family", - default="rt1170", + default="rt117x", type=click.Choice(IeeNxp.get_supported_families(), case_sensitive=False), required=True, help="Select the chip family.", @@ -1069,7 +1177,7 @@ def iee_export(config: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def iee_get_template(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -1146,7 +1254,7 @@ def bee_export(config: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def bee_get_template_command(chip_family: str, overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -1178,16 +1286,16 @@ def bootable_image_group() -> None: required=True, help="Bootable Image YAML/JSON Configuration.", ) -@click.argument("output", type=click.Path()) -def binary_image_merge_command(config: str, output: str) -> None: +@click.argument("output", type=click.Path(resolve_path=True)) +def bootable_image_merge_command(config: str, output: str) -> None: """Merge boot image blocks into one bootable image. The configuration template files could be generated by subcommand 'get-templates'. """ - binary_image_merge(config, output) + bootable_image_merge(config, output) -def binary_image_merge(config: str, output: str) -> None: +def bootable_image_merge(config: str, output: str) -> None: """Merge boot image blocks into one bootable image.""" config_data = load_configuration(config) config_dir = os.path.dirname(config) @@ -1214,7 +1322,7 @@ def binary_image_merge(config: str, output: str) -> None: "-m", "--mem-type", default="flexspi_nor", - type=click.Choice(["flexspi_nor"], case_sensitive=False), + type=click.Choice(["flexspi_nor", "flexspi_nand", "semc_nand"], case_sensitive=False), required=True, help="Select the chip used memory type.", ) @@ -1225,7 +1333,7 @@ def binary_image_merge(config: str, output: str) -> None: required=True, help="Path to binary Bootable image to parse.", ) -@click.argument("parsed-data-path", type=click.Path()) +@click.argument("parsed-data-path", type=click.Path(resolve_path=True)) def bootable_image_parse_command( chip_family: str, mem_type: str, binary: str, parsed_data_path: str ) -> None: @@ -1264,7 +1372,7 @@ def bootable_image_parse( is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output_folder", type=click.Path()) +@click.argument("output_folder", type=click.Path(resolve_path=True)) def bootable_image_get_templates_command( chip_family: str, overwrite: bool, output_folder: str ) -> None: @@ -1279,7 +1387,7 @@ def bootable_image_get_templates(chip_family: str, overwrite: bool, output_folde """Create template of configurations in YAML format from all memory types.""" mem_types = BootableImage.get_supported_memory_types(chip_family) for mem_type in mem_types: - output = os.path.join(output_folder, f"bootimg_{chip_family}_{mem_type}.yml") + output = os.path.join(output_folder, f"bootimg_{chip_family}_{mem_type}.yaml") if not os.path.isfile(output) or overwrite: click.echo(f"Creating {output} template file.") write_file(BootableImage.generate_config_template(chip_family, mem_type), output) @@ -1287,6 +1395,88 @@ def bootable_image_get_templates(chip_family: str, overwrite: bool, output_folde click.echo(f"Skip creating {output}, this file already exists.") +@main.group(name="hab", no_args_is_help=True) +def hab_group() -> None: # pylint: disable=unused-argument + """Group of sub-commands related to HAB container.""" + + +@hab_group.command(name="export", no_args_is_help=True) +@click.option( + "-c", + "--command", + type=click.Path(exists=True), + required=True, + help="BD configuration file to produce HAB container", +) +@click.option("-o", "--output", type=click.Path(resolve_path=True), help="Output file path.") +@click.argument("external", type=click.Path(), nargs=-1) +def hab_export_command( + command: str, + output: str, + external: List[str], +) -> None: + """Generate HAB container from configuration. + + EXTERNAL is a space separated list of external binary files defined in BD file + """ + image = hab_export(command, output, external) + write_file(image, output, mode="wb") + click.echo(f"Success. (HAB container: {output} created.)") + + +def hab_export(command: str, output: str, external: List[str]) -> bytes: + """Generate HAB container from configuration.""" + if output is None: + raise SPSDKAppError("Error: no output file was specified") + try: + parser = bd_parser.BDParser() + + bd_file_content = load_text(command) + bd_data = parser.parse(text=bd_file_content, extern=external) + if bd_data is None: + raise SPSDKError("Invalid bd file, secure binary file generation terminated") + hab_container = HabContainer.load( + bd_data, external=external, search_paths=[os.path.dirname(command)] + ) + data = hab_container.export() + return data + except SPSDKError as exc: + raise SPSDKAppError(f"The HAB container generation failed: ({str(exc)}).") from exc + + +@hab_group.command(name="parse", no_args_is_help=True) +@click.option( + "-b", + "--binary", + type=click.Path(exists=True, readable=True, resolve_path=True), + required=True, + help="Path to binary HAB image to parse.", +) +@click.argument("output_folder", type=click.Path(resolve_path=True)) +def hab_parse_command( + binary: str, + output_folder: str, +) -> None: + """Parse HAb contianer into individual segments.""" + file_bin = load_binary(binary) + created_files = hab_parse(file_bin, output_folder) + for file_path in created_files: + click.echo(f"File has been created: {file_path}") + click.echo(f"Success. (HAB container parsed into: {output_folder}.)") + + +def hab_parse(binary: bytes, output: str) -> List[str]: + """Generate HAB container from configuration.""" + hab_container = HabContainer.parse(binary) + created_files = [] + for sub_image in hab_container.binary_image.sub_images: + sub_image_data = sub_image.export() + sub_image_path = os.path.join(output, f"{sub_image.name.lower()}.bin") + write_file(sub_image_data, sub_image_path, mode="wb") + created_files.append(sub_image_path) + return created_files + + @bootable_image_group.group(name="fcb", no_args_is_help=True) def fcb() -> None: # pylint: disable=unused-argument """FCB (Flash Configuration Block) utilities.""" @@ -1300,7 +1490,7 @@ def fcb() -> None: # pylint: disable=unused-argument required=True, help="FCB YAML/JSON Configuration.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def fcb_export_command(config: str, output: str) -> None: """Export FCB Image from YAML/JSON configuration. @@ -1347,11 +1537,11 @@ def fcb_export(config: str, output: str) -> None: @click.option( "-b", "--binary", - type=click.Path(exists=True, readable=True), + type=click.Path(exists=True, readable=True, resolve_path=True), required=True, help="Path to binary FCB image to parse.", ) -@click.argument("config-file", type=click.Path()) +@click.argument("config-file", type=click.Path(resolve_path=True)) def fcb_parse_command(chip_family: str, mem_type: str, binary: str, config_file: str) -> None: """Parse FCB Image into YAML configuration.""" fcb_parse(chip_family, mem_type, binary, config_file) @@ -1385,7 +1575,7 @@ def fcb_parse(chip_family: str, mem_type: str, binary: str, config_file: str) -> is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output_folder", type=click.Path()) +@click.argument("output_folder", type=click.Path(resolve_path=True)) def fcb_get_templates_command(chip_family: str, overwrite: bool, output_folder: str) -> None: """Create template of configurations in YAML format for all memory types. @@ -1398,7 +1588,7 @@ def fcb_get_templates(chip_family: str, overwrite: bool, output_folder: str) -> """Create template of configurations in YAML format for all memory types.""" mem_types = FCB.get_supported_memory_types(chip_family) for mem_type in mem_types: - output = os.path.join(output_folder, f"fcb_{chip_family}_{mem_type}.yml") + output = os.path.join(output_folder, f"fcb_{chip_family}_{mem_type}.yaml") if not os.path.isfile(output) or overwrite: click.echo(f"Creating {output} template file.") write_file(FCB.generate_config_template(chip_family, mem_type), output) @@ -1419,7 +1609,7 @@ def xmcd() -> None: # pylint: disable=unused-argument required=True, help="XMCD YAML/JSON Configuration.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def xmcd_export_command(config: str, output: str) -> None: """Export XMCD Image from YAML/JSON configuration. @@ -1454,11 +1644,11 @@ def xmcd_export(config: str, output: str) -> None: @click.option( "-b", "--binary", - type=click.Path(exists=True, readable=True), + type=click.Path(exists=True, readable=True, resolve_path=True), required=True, help="Path to binary XMCD image to parse.", ) -@click.argument("config-file", type=click.Path()) +@click.argument("config-file", type=click.Path(resolve_path=True)) def xmcd_parse_command(chip_family: str, binary: str, config_file: str) -> None: """Parse XMCD Image into YAML configuration.""" xmcd_parse(chip_family, binary, config_file) @@ -1492,7 +1682,7 @@ def xmcd_parse(chip_family: str, binary: str, config_file: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output_folder", type=click.Path()) +@click.argument("output_folder", type=click.Path(resolve_path=True)) def xmcd_get_templates_command(chip_family: str, overwrite: bool, output_folder: str) -> None: """Create template of configurations in YAML format for all memory types. @@ -1507,7 +1697,9 @@ def xmcd_get_templates(chip_family: str, overwrite: bool, output_folder: str) -> for mem_type in mem_types: config_types = XMCD.get_supported_configuration_types(chip_family, mem_type) for config_type in config_types: - output = os.path.join(output_folder, f"xmcd_{chip_family}_{mem_type}_{config_type}.yml") + output = os.path.join( + output_folder, f"xmcd_{chip_family}_{mem_type}_{config_type}.yaml" + ) if not os.path.isfile(output) or overwrite: click.echo(f"Creating {output} template file.") write_file( @@ -1542,7 +1734,7 @@ def bin_image_group() -> None: default="zeros", help="Pattern of created file ('zeros', 'ones', 'rand', 'inc' or any number value).", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def binary_create_command(size: int, pattern: str, output: str) -> None: """Create binary file with pattern. @@ -1572,7 +1764,7 @@ def binary_create(size: int, pattern: str, output: str) -> None: required=True, help="Path to configuration file of merge operation.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def binary_merge_command(config: str, output: str) -> None: """Merge binary images together by description in YAML/JSON configuration. @@ -1587,7 +1779,11 @@ def binary_merge(config: str, output: str) -> None: config_dir = os.path.dirname(config) check_config(cfg, BinaryImage.get_validation_schemas(), search_paths=[config_dir]) image = BinaryImage.load_from_config(cfg, search_paths=[config_dir]) - image.validate() + try: + image.validate() + except SPSDKError as exc: + click.echo(f"Image Validation fail:\n{image.draw()}") + raise SPSDKError("Image validation failed") from exc data = image.export() write_file(data, output, mode="wb") @@ -1619,7 +1815,7 @@ def binary_merge(config: str, output: str) -> None: required=True, help="Size of extracted chunk. For '0' it extract rest of the file from given address.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def binary_extract_command(binary: str, address: str, size: str, output: str) -> None: """Extract chunk from binary file. @@ -1661,7 +1857,7 @@ def binary_extract(binary: str, address: str, size: str, output: str) -> None: required=True, help="Output format.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def binary_convert_command(input_file: str, output_format: str, output: str) -> None: """Convert input data file into selected format. @@ -1688,7 +1884,7 @@ def binary_convert(input_file: str, output_format: str, output: str) -> None: is_flag=True, help="Allow overwriting existing template file.", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def binary_get_template_command(overwrite: bool, output: str) -> None: """Create template of configuration in YAML format. @@ -1706,6 +1902,123 @@ def binary_get_template(overwrite: bool, output: str) -> None: click.echo(f"Skip creating {output}, this file already exists.") +@bin_image_group.command(name="align", no_args_is_help=True) +@click.option( + "-i", + "--input", + type=click.Path(file_okay=True, dir_okay=False, resolve_path=True), + required=True, + help="Binary file name to be aligned.", +) +@click.option( + "-o", + "--output", + type=click.Path(resolve_path=True), + help="Aligned file name. If not specified, input file will be updated.", +) +@click.option( + "-a", + "--alignment", + default=1, + type=INT(), + help="Alignment size.", +) +@click.option( + "-p", + "--pattern", + type=str, + default="zeros", + help="Pattern of used padding ('zeros', 'ones', 'rand', 'inc' or any number value).", +) +def binary_align_command(input: str, output: str, alignment: int, pattern: str) -> None: + """Align binary file to provided alignment and padded with specified pattern.""" + binary_align(input, output, alignment, pattern) + + +def binary_align( + input: str, output: Optional[str] = None, alignment: int = 1, pattern: str = "zeros" +) -> None: + """Align binary file to provided alignment and padded with specified pattern. + + :param input: Input file name. + :param output: Output file name. If not specified, input file will be updated. + :param alignment: Size of alignment block. + :param pattern: Padding pattern. + + :raises SPSDKError: Invalid input arguments. + """ + if not input or not os.path.isfile(input): + raise SPSDKError("Binary file alignment: Invalid input file") + if alignment <= 0: + raise SPSDKError("Binary file alignment: Alignment value must be 1 or greater.") + if not output: + output = input + binary = align_block(data=load_binary(input), alignment=alignment, padding=pattern) + write_file(binary, output, mode="wb") + click.echo( + f"The {input} file has been aligned to {len(binary)} ({hex(len(binary))}) bytes and stored into {output}" + ) + + +@bin_image_group.command(name="pad", no_args_is_help=True) +@click.option( + "-i", + "--input", + type=click.Path(file_okay=True, dir_okay=False, resolve_path=True), + required=True, + help="Binary file name to be padded.", +) +@click.option( + "-o", + "--output", + type=click.Path(resolve_path=True), + help="Padded file name. If not specified, input file will be updated.", +) +@click.option( + "-s", + "--size", + required=True, + type=INT(), + help="Result file size.", +) +@click.option( + "-p", + "--pattern", + type=str, + default="zeros", + help="Pattern of used padding ('zeros', 'ones', 'rand', 'inc' or any number value).", +) +def binary_pad_command(input: str, output: str, size: int, pattern: str) -> None: + """Pad binary file to provided final size with specified pattern.""" + binary_pad(input, output, size, pattern) + + +def binary_pad( + input: str, output: Optional[str] = None, size: int = 1, pattern: str = "zeros" +) -> None: + """Pad binary file to provided final size with specified pattern. + + :param input: Input file name. + :param output: Output file name. If not specified, input file will be updated. + :param size: Final size of file. + :param pattern: Padding pattern. + + :raises SPSDKError: Invalid input arguments. + """ + if not input or not os.path.isfile(input): + raise SPSDKError("Binary file alignment: Invalid input file") + if not output: + output = input + binary = load_binary(input) + if len(binary) < size: + binary = align_block(data=binary, alignment=size, padding=pattern) + write_file(binary, output, mode="wb") + + click.echo( + f"The {input} file has been padded to {len(binary)} ({hex(len(binary))}) byte size and stored into {output}" + ) + + @utils_group.group(name="convert", no_args_is_help=True) def convert() -> None: # pylint: disable=unused-argument """Conversion utilities.""" @@ -1727,7 +2040,7 @@ def convert() -> None: # pylint: disable=unused-argument default=False, help="The resulting binary bytes will be stored in reverse order (for example SBKEK in elftosb requires that).", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def convert_hex2bin_command(input_file: str, reverse: bool, output: str) -> None: """Convert file with hexadecimal string into binary file with optional reverse order of stored bytes. @@ -1750,7 +2063,12 @@ def convert_hex2bin(input_file: str, reverse: bool, output: str) -> None: click.echo(f"Success. Converted file: {output}") -@convert.command(name="bin2hex", no_args_is_help=True, short_help="AAAA", help="BBBBB") +@convert.command( + name="bin2hex", + no_args_is_help=True, + short_help="Convert binary file to hexadecimal text file.", + help="Convert binary file into hexadecimal text file with optional reverse order of stored bytes.", +) @click.option( "-i", "--input-file", @@ -1766,7 +2084,7 @@ def convert_hex2bin(input_file: str, reverse: bool, output: str) -> None: default=False, help="The result binary bytes will be stored in reverse order (for example SBKEK in elftosb this required).", ) -@click.argument("output", type=click.Path()) +@click.argument("output", type=click.Path(resolve_path=True)) def convert_bin2hex_command(input_file: str, reverse: bool, output: str) -> None: """Convert binary file into hexadecimal text file with optional reverse order of stored bytes. @@ -1835,7 +2153,7 @@ def convert_bin2hex(input_file: str, reverse: bool, output: str) -> None: @click.option( "-o", "--output-file", - type=click.Path(), + type=click.Path(resolve_path=True), help=( "Path to output text file with created C array, " "if not specified the output will be printed into standard output." @@ -1874,7 +2192,7 @@ def convert_bin2carr( line_cnt = min(count_per_line, rest_bytes // width) comment = f"// 0x{index:09_X}" ret += " " * tab - for i in range(line_cnt): + for _ in range(line_cnt): data = bytearray(binary[index : index + width]) if endian.lower() == "little": data.reverse() @@ -1886,6 +2204,7 @@ def convert_bin2carr( if output_file: write_file(ret, output_file) + click.echo(f"Success. Created C file: {output_file}") else: click.echo(ret) diff --git a/spsdk/apps/nxpkeygen.py b/spsdk/apps/nxpkeygen.py index ca5b5456..110b923c 100644 --- a/spsdk/apps/nxpkeygen.py +++ b/spsdk/apps/nxpkeygen.py @@ -72,7 +72,7 @@ def get_list_of_supported_keys() -> List[str]: help="Password with which the output file will be encrypted. " "If not provided, the output will be unencrypted.", ) -@click.argument("path", type=click.Path(file_okay=True)) +@click.argument("path", type=click.Path(file_okay=True, resolve_path=True)) @click.option( "--force", is_flag=True, @@ -111,6 +111,7 @@ def main(log_level: int, key_type: str, path: str, password: str, force: bool) - logger.info("Saving ECC key pair...") save_ecc_private_key(priv_key_ec, path, password if password else None) save_ecc_public_key(pub_key_ec, pub_key_path) + click.echo(f"The key pair has been created: {(pub_key_path)}, {(path)}") return 0 diff --git a/spsdk/apps/pfr.py b/spsdk/apps/pfr.py index fa13d766..5d182891 100644 --- a/spsdk/apps/pfr.py +++ b/spsdk/apps/pfr.py @@ -10,7 +10,7 @@ import logging import os import sys -from typing import Optional, Tuple, Type, Union +from typing import Callable, Optional, Tuple, Type, Union import click from click_option_group import MutuallyExclusiveOptionGroup, optgroup @@ -28,18 +28,23 @@ from spsdk.mboot import McuBoot from spsdk.pfr.exceptions import SPSDKError, SPSDKPfrConfigError, SPSDKPfrError from spsdk.pfr.pfrc import Pfrc -from spsdk.utils.misc import find_file, load_configuration, size_fmt, write_file +from spsdk.utils.misc import find_file, load_binary, load_configuration, size_fmt, write_file from spsdk.utils.schema_validator import ConfigTemplate PFRArea = Union[Type[pfr.CMPA], Type[pfr.CFPA]] logger = logging.getLogger(__name__) -def _store_output(data: Union[str, bytes], path: Optional[str], mode: str = "w") -> None: +def _store_output( + data: Union[str, bytes], path: Optional[str], mode: str = "w", msg: Optional[str] = None +) -> None: """Store the output data; either on stdout or into file if it's provided.""" + if msg: + click.echo(msg) if path is None: click.echo(data) else: + click.echo(f"Result has been stored in: {path}") write_file(data, path=path, mode=mode) @@ -48,29 +53,39 @@ def _get_pfr_class(area_name: str) -> PFRArea: return getattr(pfr, area_name.upper()) -def pfr_device_type_options(options: FC) -> FC: - """Setup PFR options for device, revision and area (PFR page type).""" - options = click.option( - "-r", - "--revision", - help="Chip revision; if not specified, most recent one will be used", - )(options) - options = click.option( - "-d", - "--device", - type=click.Choice(pfr.CMPA.devices()), - help="Device to use", - required=True, - )(options) - options = click.option( - "-t", - "--type", - "area", - required=True, - type=click.Choice(["cmpa", "cfpa"]), - help="Select PFR partition", - )(options) - return options +def pfr_device_type_options(no_type: bool = False) -> Callable: + """PFR common device click options. + + :param no_type: If true, the type option is not added, defaults to False + :return: Click decorator + """ + + def decorator(options: Callable[[FC], FC]) -> Callable[[FC], FC]: + """Setup PFR options for device, revision and area (PFR page type).""" + options = click.option( + "-r", + "--revision", + help="Chip revision; if not specified, most recent one will be used", + )(options) + options = click.option( + "-d", + "--device", + type=click.Choice(pfr.CMPA.devices()), + help="Device to use", + required=True, + )(options) + if not no_type: + options = click.option( + "-t", + "--type", + "area", + required=True, + type=click.Choice(["cmpa", "cfpa"]), + help="Select PFR partition", + )(options) + return options + + return decorator @click.group(name="pfr", no_args_is_help=True, cls=CommandsTreeGroupAliasedGetCfgTemplate) @@ -82,11 +97,11 @@ def main(log_level: int) -> int: @main.command(name="get-template", no_args_is_help=True) -@pfr_device_type_options +@pfr_device_type_options() @click.option( "-o", "--output", - type=click.Path(), + type=click.Path(resolve_path=True), required=False, help="Save the output into a file instead of console", ) @@ -96,11 +111,11 @@ def get_template(device: str, revision: str, area: str, output: str, full: bool) pfr_obj = _get_pfr_class(area)(device=device, revision=revision) data = pfr_obj.get_yaml_config(not full) yaml_data = ConfigTemplate.convert_cm_to_yaml(data) - _store_output(yaml_data, output) + _store_output(yaml_data, output, msg=f"PFR {area} configuration template has been created.") @main.command(name="parse-binary", no_args_is_help=True) -@pfr_device_type_options +@pfr_device_type_options() @click.option( "-o", "--output", @@ -111,7 +126,7 @@ def get_template(device: str, revision: str, area: str, output: str, full: bool) @click.option( "-b", "--binary", - type=click.Path(exists=True, dir_okay=False), + type=click.Path(exists=True, dir_okay=False, resolve_path=True), required=True, help="Binary to parse", ) @@ -137,8 +152,7 @@ def parse_binary( show_diff: bool, ) -> None: """Parse binary and extract configuration.""" - with open(binary, "rb") as f: - data = f.read() + data = load_binary(binary) yaml_data = _parse_binary_data( data=data, device=device, @@ -147,8 +161,7 @@ def parse_binary( show_calc=show_calc, show_diff=show_diff, ) - _store_output(yaml_data, output) - click.echo(f"Success. (PFR: {binary} has been parsed and stored into {output}.)") + _store_output(yaml_data, output, msg=f"Success. (PFR: {binary} has been parsed.") def _parse_binary_data( @@ -202,7 +215,7 @@ def _parse_binary_data( @click.option( "-o", "--output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), required=True, help="Save the output into a file instead of console", ) @@ -279,16 +292,15 @@ def generate_binary( if not pfr_config.revision: pfr_config.revision = pfr_obj.revision data = pfr_obj.export(add_seal=add_seal, keys=keys) - _store_output(data, output, "wb") - click.echo(f"Success. (PFR binary has been generated into {output}.)") + _store_output(data, output, "wb", msg=f"Success. (PFR binary has been generated)") @main.command(name="info", no_args_is_help=True) -@pfr_device_type_options +@pfr_device_type_options() @click.option( "-o", "--output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), required=True, help="Save the output into a file instead of console", ) @@ -308,7 +320,7 @@ def info(device: str, revision: str, area: str, output: str, open_result: bool) regs_exclude=["SHA256_DIGEST"], fields_exclude=["FIELD"], ) - _store_output(html_output, output) + _store_output(html_output, output, msg=f"Success. (PFR info HTML page has been generated)") if open_result: # pragma: no cover # can't test opening the html document click.launch(f"{output}") @@ -328,7 +340,7 @@ def devices() -> None: json_option=False, use_long_timeout_option=True, ) -@pfr_device_type_options +@pfr_device_type_options() @click.option( "-b", "--binary", @@ -354,8 +366,7 @@ def write( click.echo(f"{pfr_obj.__class__.__name__} page address on {device} is {pfr_page_address:#x}") - with open(binary, "rb") as f: - data = f.read() + data = load_binary(binary) if len(data) != pfr_page_length: raise SPSDKError( f"PFR page length is {pfr_page_length}. Provided binary has {size_fmt(len(data))}." @@ -381,18 +392,18 @@ def write( json_option=False, use_long_timeout_option=True, ) -@pfr_device_type_options +@pfr_device_type_options() @click.option( "-o", "--output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), help="Store PFR data into a file. If not specified hexdump data into stdout.", ) @click.option( "-y", "--yaml", "yaml_output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), help="Parse data read from device into YAML config.", ) @click.option( @@ -457,6 +468,41 @@ def read( click.echo(format_raw_data(data=data, use_hexdump=True)) +@main.command(name="erase-cmpa", no_args_is_help=True) +@isp_interfaces( + uart=True, + usb=True, + lpcusbsio=True, + buspal=True, + json_option=False, + use_long_timeout_option=True, +) +@pfr_device_type_options(no_type=True) +def erase_cmpa( + port: str, + usb: str, + buspal: str, + lpcusbsio: str, + timeout: int, + device: str, + revision: str, +) -> None: + """Erase CMPA PFR page in the device if is not sealed.""" + pfr_obj = _get_pfr_class("cmpa")(device=device, revision=revision) + pfr_page_address = pfr_obj.config.get_address(device) + pfr_page_length = pfr_obj.BINARY_SIZE + + click.echo(f"CMPA page address on {device} is {pfr_page_address:#x}") + + interface = get_interface( + module="mboot", port=port, usb=usb, buspal=buspal, lpcusbsio=lpcusbsio, timeout=timeout + ) + assert isinstance(interface, MBootInterface) + with McuBoot(device=interface) as mboot: + mboot.write_memory(address=pfr_page_address, data=bytes(pfr_page_length)) + click.echo(f"CMPA page {'has been erased.' if mboot.status_code == 0 else 'erase failed!'}") + + @catch_spsdk_error def safe_main() -> None: """Call the main function.""" diff --git a/spsdk/apps/shadowregs.py b/spsdk/apps/shadowregs.py index c6201300..b1bdf345 100644 --- a/spsdk/apps/shadowregs.py +++ b/spsdk/apps/shadowregs.py @@ -71,16 +71,13 @@ def _open_shadow_registers(pass_obj: Dict, connect: bool = True) -> Iterator[Sha if not connect: yield ShadowRegisters(debug_probe=None, config=regs_cfg, device=device, revision=revision) else: - try: - with _open_debug_probe(pass_obj) as debug_probe: - if not enable_debug(debug_probe): - raise SPSDKError("Cannot enable debug interface") + with _open_debug_probe(pass_obj) as debug_probe: + if not enable_debug(debug_probe): + raise SPSDKError("Cannot enable debug interface") - yield ShadowRegisters( - debug_probe=debug_probe, config=regs_cfg, device=device, revision=revision - ) - except SPSDKError as exc: - raise SPSDKError(f"Error with opening debug probe: ({str(exc)})") from exc + yield ShadowRegisters( + debug_probe=debug_probe, config=regs_cfg, device=device, revision=revision + ) @click.group(name="shadowregs", no_args_is_help=True, cls=CommandsTreeGroup) @@ -149,9 +146,10 @@ def main( @click.option( "-f", "--filename", - default="sr_config.yml", + type=click.Path(resolve_path=True), + default="sr_config.yaml", help="The name of file used to save the current configuration." - " Default name is 'sr_config'. The extension is always '.yml'.", + " Default name is 'sr_config'. The extension is always '.yaml'.", ) @click.option( "-r", @@ -170,23 +168,24 @@ def main( ) @click.pass_obj def saveconfig(pass_obj: dict, filename: str, raw: bool, save_diff: bool) -> None: - """Save current state of shadow registers to YML file.""" + """Save current state of shadow registers to YAML file.""" try: with _open_shadow_registers(pass_obj) as shadow_regs: shadow_regs.reload_registers() - shadow_regs.create_yml_config(filename, raw, diff=save_diff) + shadow_regs.create_yaml_config(filename, raw, diff=save_diff) click.echo(f"The Shadow registers has been saved into {filename} YAML file") except SPSDKError as exc: raise SPSDKError(f"Save configuration of Shadow registers failed! ({str(exc)})") from exc -@main.command() +@main.command(no_args_is_help=True) @click.option( "-f", "--filename", - default="sr_config.yml", + type=click.Path(resolve_path=True), + required=True, help="The name of file used to load a new configuration." - " Default name is 'sr_config'. The extension is always '.yml'.", + " Default name is 'sr_config'. The extension is always '.yaml'.", ) @click.option( "-r", @@ -196,20 +195,25 @@ def saveconfig(pass_obj: dict, filename: str, raw: bool, save_diff: bool) -> Non help="In loaded configuration will accepted also the computed fields " "and anti-pole registers.", ) +@click.option( + "--verify/--no-verify", + is_flag=True, + default=True, + help="Verify write operation (verify by default)", +) @click.pass_obj -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.""" +def loadconfig(pass_obj: dict, filename: str, raw: bool, verify: bool) -> None: + """Load new state of shadow registers from YAML file into microcontroller.""" try: with _open_shadow_registers(pass_obj) as shadow_regs: - shadow_regs.load_yml_config(filename, raw) - shadow_regs.sets_all_registers() + shadow_regs.load_yaml_config(filename, raw) + shadow_regs.sets_all_registers(verify) click.echo(f"The Shadow registers has been loaded by configuration in {filename} YAML file") except SPSDKError as exc: raise SPSDKError(f"Load configuration of Shadow registers failed ({str(exc)})!") from exc @main.command(name="get-template", no_args_is_help=True) -@click.argument("output", metavar="PATH", type=click.Path()) @click.option( "-r", "--raw", @@ -218,15 +222,16 @@ def loadconfig(pass_obj: dict, filename: str = "sr_config.yml", raw: bool = Fals help="In loaded configuration will accepted also the computed fields " "and anti-pole registers.", ) +@click.argument("output", metavar="PATH", type=click.Path(resolve_path=True)) @click.pass_obj def get_template(pass_obj: dict, output: str, raw: bool) -> None: - """Generate the template of Shadow registers YML configuration file. + """Generate the template of Shadow registers YAML configuration file. \b PATH - file name path to write template config file """ with _open_shadow_registers(pass_obj, connect=False) as shadow_regs: - shadow_regs.create_yml_config(output, raw) + shadow_regs.create_yaml_config(output, raw) click.echo( f"The Shadow registers template for {pass_obj['device']} has been saved into {output} YAML file" ) @@ -263,8 +268,8 @@ def printregs(pass_obj: dict, rich: bool = False) -> None: raise SPSDKError(f"Print of Shadow registers failed! ({str(exc)})") from exc -@main.command() -@click.option("-r", "--reg", type=str, help="The name of register to be read.") +@main.command(no_args_is_help=True) +@click.option("-r", "--reg", type=str, required=True, help="The name of register to be read.") @click.pass_obj def getreg(pass_obj: dict, reg: str) -> None: """The command prints the current value of one shadow register.""" @@ -277,15 +282,23 @@ def getreg(pass_obj: dict, reg: str) -> None: raise SPSDKError(f"Getting Shadow register failed! ({str(exc)})") from exc -@main.command() -@click.option("-r", "--reg", type=str, help="The name of register to be set.") -@click.option("-v", "--reg-val", type=str, help="The new value of register in hex format.") +@main.command(no_args_is_help=True) +@click.option("-r", "--reg", type=str, required=True, help="The name of register to be set.") +@click.option( + "-v", "--reg-val", type=str, required=True, help="The new value of register in hex format." +) +@click.option( + "--verify/--no-verify", + is_flag=True, + default=True, + help="Verify write operation (verify by default)", +) @click.pass_obj -def setreg(pass_obj: dict, reg: str, reg_val: str) -> None: +def setreg(pass_obj: dict, reg: str, reg_val: str, verify: bool) -> None: """The command sets a value of one shadow register defined by parameter.""" try: with _open_shadow_registers(pass_obj) as shadow_regs: - shadow_regs.set_register(reg, reg_val) + shadow_regs.set_register(reg, reg_val, verify) click.echo(f"The Shadow register {reg} has been set to {reg_val} value") except SPSDKError as exc: raise SPSDKError(f"Setting Shadow register failed! ({str(exc)})") from exc @@ -304,9 +317,9 @@ def reset(pass_obj: dict) -> None: @click.option( "-o", "--output", - type=click.Path(), + type=click.Path(resolve_path=True), required=True, - help="Save the output into a file instead of console", + help="File name of generated output HTML description file", ) @click.option( "-p", @@ -347,7 +360,7 @@ def info(pass_obj: dict, output: str, open_result: bool) -> None: ) @click.argument( "output", - type=click.Path(), + type=click.Path(resolve_path=True), required=True, ) @click.pass_obj @@ -358,7 +371,7 @@ def fuses_script(pass_obj: dict, config: str, output: str) -> None: """ try: with _open_shadow_registers(pass_obj, connect=False) as shadow_regs: - shadow_regs.load_yml_config(config, False) + shadow_regs.load_yaml_config(config, False) cfg = load_configuration(config) write_file(shadow_regs.create_fuse_blhost_script(list(cfg["registers"].keys())), output) click.echo(f"BLHOST script to burn fuses has been generated: {output}") diff --git a/spsdk/apps/tp_utils.py b/spsdk/apps/tp_utils.py index 854644ca..e6143594 100644 --- a/spsdk/apps/tp_utils.py +++ b/spsdk/apps/tp_utils.py @@ -13,7 +13,6 @@ import click import colorama import prettytable -import yaml from typing_extensions import Literal from spsdk import SPSDKError @@ -32,7 +31,7 @@ single_tp_device_adapter, single_tp_target_adapter, ) -from spsdk.utils.misc import find_file +from spsdk.utils.misc import find_file, load_binary, load_configuration from spsdk.utils.schema_validator import ValidationSchemas, check_config @@ -85,11 +84,8 @@ def _validate(self) -> None: def _get_config_data(config_file_path: Optional[str] = None) -> Dict[str, Any]: """Setup initial configuration data.""" if config_file_path: - with open(config_file_path) as f: - return yaml.safe_load(f) - return { - "timeout": 60, - } + return load_configuration(config_file_path) + return {"timeout": 60} @property def tp_device(self) -> str: @@ -222,8 +218,7 @@ def firmware_data(self) -> Optional[bytes]: self.config_data["firmware"], search_paths=[self.config_dir] if self.config_dir else None, ) - with open(file_path, "rb") as f: - return f.read() + return load_binary(file_path) @property def prov_firmware_data(self) -> Optional[bytes]: @@ -234,8 +229,7 @@ def prov_firmware_data(self) -> Optional[bytes]: self.config_data["prov_firmware"], search_paths=[self.config_dir] if self.config_dir else None, ) - with open(file_path, "rb") as f: - return f.read() + return load_binary(file_path) class TPConfigConfig(TPBaseConfig): diff --git a/spsdk/apps/tpconfig.py b/spsdk/apps/tpconfig.py index 5b6ef75b..dec0a45d 100644 --- a/spsdk/apps/tpconfig.py +++ b/spsdk/apps/tpconfig.py @@ -29,6 +29,7 @@ ) from spsdk.tp import TP_DATA_FOLDER, SPSDKTpError, TpDevInterface, TrustProvisioningConfig from spsdk.tp.utils import get_supported_devices, scan_tp_devices +from spsdk.utils.misc import load_text, write_file @click.group(name="tpconfig", cls=CommandsTreeGroupAliasedGetCfgTemplate) @@ -155,20 +156,17 @@ def seal( @click.option( "-o", "--output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), required=True, help="The output YAML template configuration file name.", ) # pylint: disable=unused-argument # preparation for the future def get_template(family: str, output: str) -> None: """Command to generate tphost template of configuration YML file.""" - with open(os.path.join(TP_DATA_FOLDER, "tpconfig_cfg_template.yml"), "r") as file: - template = file.read() + template = load_text(os.path.join(TP_DATA_FOLDER, "tpconfig_cfg_template.yaml")) + write_file(template, output) - with open(str(output), "w") as file: - file.write(template) - - click.echo(f"The configuration template created. {os.path.abspath(output)}") + click.echo(f"The configuration template created. {output}") main.add_command(device_help) diff --git a/spsdk/apps/tphost.py b/spsdk/apps/tphost.py index 8993ed4d..2397c48f 100644 --- a/spsdk/apps/tphost.py +++ b/spsdk/apps/tphost.py @@ -42,6 +42,7 @@ scan_tp_devices, scan_tp_targets, ) +from spsdk.utils.misc import load_binary, load_text, write_file @click.group(name="tphost", cls=CommandsTreeGroupAliasedGetCfgTemplate) @@ -247,7 +248,7 @@ def load_tpfw( @click.option( "-o", "--output", - type=click.Path(dir_okay=False), + type=click.Path(dir_okay=False, resolve_path=True), required=True, help="The output YAML template configuration file name.", ) @@ -257,13 +258,10 @@ def get_template( output: str, ) -> None: """Command to generate tphost template of configuration YML file.""" - with open(os.path.join(TP_DATA_FOLDER, "tphost_cfg_template.yml"), "r") as file: - template = file.read() - - with open(str(output), "w") as file: - file.write(template) + template = load_text(os.path.join(TP_DATA_FOLDER, "tphost_cfg_template.yaml")) + write_file(template, output) - click.echo(f"The configuration template created. {os.path.abspath(output)}") + click.echo(f"The configuration template created. {output}") @main.command(name="verify", no_args_is_help=True) @@ -538,8 +536,7 @@ def check_cot(root_cert: str, intermediate_cert: str, tp_response: str) -> None: message = "Parsing TP_RESPONSE data container..." try: - with open(tp_response, "rb") as f: - tp_data = f.read() + tp_data = load_binary(tp_response) tp_response_container = Container.parse(tp_data) message += "OK" except Exception: diff --git a/spsdk/crypto/__init__.py b/spsdk/crypto/__init__.py index c4977796..c5873556 100644 --- a/spsdk/crypto/__init__.py +++ b/spsdk/crypto/__init__.py @@ -115,4 +115,3 @@ # Explicit import due to MYPY issue from .loaders import load_certificate, load_certificate_as_bytes, load_private_key, load_public_key -from .signature_provider import SignatureProvider diff --git a/spsdk/crypto/keys_management.py b/spsdk/crypto/keys_management.py index d734a2e1..6a6c9ff4 100644 --- a/spsdk/crypto/keys_management.py +++ b/spsdk/crypto/keys_management.py @@ -7,7 +7,7 @@ """Module for key generation and saving keys to file (RSA and ECC).""" from enum import Enum -from typing import Optional, Union +from typing import Optional from spsdk.crypto import ( EllipticCurvePrivateKeyWithSerialization, diff --git a/spsdk/crypto/loaders.py b/spsdk/crypto/loaders.py index 3a192c47..cdc59110 100644 --- a/spsdk/crypto/loaders.py +++ b/spsdk/crypto/loaders.py @@ -44,7 +44,10 @@ def load_private_key_from_data( real_encoding = encoding or _get_encoding_type(data) try: - private_key = {Encoding.PEM: load_pem_private_key, Encoding.DER: load_der_private_key,}[ + private_key = { + Encoding.PEM: load_pem_private_key, + Encoding.DER: load_der_private_key, + }[ real_encoding ](data, password, default_backend()) assert isinstance(private_key, (RSAPrivateKey, EllipticCurvePrivateKey)) @@ -78,7 +81,10 @@ def load_public_key_from_data(data: bytes, encoding: Optional[Encoding] = None) real_encoding = encoding or _get_encoding_type(data) try: - public_key = {Encoding.PEM: load_pem_public_key, Encoding.DER: load_der_public_key,}[ + public_key = { + Encoding.PEM: load_pem_public_key, + Encoding.DER: load_der_public_key, + }[ real_encoding ](data, default_backend()) assert isinstance(public_key, (RSAPublicKey, EllipticCurvePublicKey)) @@ -108,7 +114,10 @@ def load_certificate_from_data(data: bytes, encoding: Optional[Encoding] = None) """ real_encoding = encoding or _get_encoding_type(data) try: - return {Encoding.PEM: load_pem_x509_certificate, Encoding.DER: load_der_x509_certificate,}[ + return { + Encoding.PEM: load_pem_x509_certificate, + Encoding.DER: load_der_x509_certificate, + }[ real_encoding ](data, default_backend()) except ValueError as exc: diff --git a/spsdk/crypto/signature_provider.py b/spsdk/crypto/signature_provider.py index c89d81fc..4ce696e1 100644 --- a/spsdk/crypto/signature_provider.py +++ b/spsdk/crypto/signature_provider.py @@ -13,10 +13,18 @@ """ import abc -from typing import Dict, List, Optional +import logging +from typing import Any, Dict, List, Optional, Union + +from Crypto.PublicKey import ECC from spsdk import crypto -from spsdk.exceptions import SPSDKError +from spsdk.exceptions import SPSDKError, SPSDKUnsupportedOperation, SPSDKValueError +from spsdk.utils.crypto.backend_internal import internal_backend +from spsdk.utils.crypto.common import crypto_backend +from spsdk.utils.misc import find_file + +logger = logging.getLogger(__name__) class SignatureProvider(abc.ABC): @@ -26,25 +34,34 @@ class SignatureProvider(abc.ABC): sp_type = "INVALID" @abc.abstractmethod - def sign(self, data: bytes) -> Optional[bytes]: + def sign(self, data: bytes) -> bytes: """Return signature for data.""" - @abc.abstractmethod - def info(self) -> str: - """Provide information about the Signature provide.""" - @property @abc.abstractmethod def signature_length(self) -> int: """Return length of the signature.""" + def verify_public_key(self, public_key: bytes) -> bool: + """Verify if given public key matches private key.""" + raise SPSDKUnsupportedOperation("Verify method is not supported.") + + def info(self) -> str: + """Provide information about the Signature provider.""" + return self.__class__.__name__ + @staticmethod - def _convert_params(params: str) -> Dict[str, str]: + def convert_params(params: str) -> Dict[str, str]: """Coverts creation params from string into dictionary. e.g.: "type=file;file_path=some_path" -> {'type': 'file', 'file_path': 'some_path'} """ - result = dict([tuple(p.split("=")) for p in params.split(";")]) # type: ignore #oh dear Mypy + try: + result = dict([tuple(p.split("=")) for p in params.split(";")]) # type: ignore #oh dear Mypy + except ValueError as e: + raise SPSDKValueError( + "Parameter must meet following pattern: type=file;file_path=some_path" + ) from e return result @classmethod @@ -53,14 +70,22 @@ def get_types(cls) -> List[str]: return [sub_class.sp_type for sub_class in cls.__subclasses__()] @classmethod - def create(cls, create_params: str) -> Optional["SignatureProvider"]: + def create(cls, params: Union[str, dict]) -> Optional["SignatureProvider"]: """Creates an concrete instance of signature provider.""" - params = cls._convert_params(create_params) + if isinstance(params, str): + params = cls.convert_params(params) for ( klass ) in cls.__subclasses__(): # pragma: no branch # there always be at least one subclass if klass.sp_type == params["type"]: del params["type"] + unused_params = set(params) - set(klass.__init__.__code__.co_varnames) + for unused_param in unused_params: + if unused_param != "search_paths": + logger.warning( + f"Removing unused parameter for {klass.sp_type} signature provider: {unused_param}" + ) + del params[unused_param] return klass(**params) # type: ignore #oh dear Mypy return None @@ -76,6 +101,8 @@ def __init__( password: str = "", # pylint: disable=unused-argument encoding: str = "PEM", # pylint: disable=unused-argument hash_alg: Optional[str] = None, + search_paths: Optional[List[str]] = None, + mode: Optional[str] = None, ) -> None: """Initialize the plain file signature provider. @@ -83,10 +110,13 @@ def __init__( :param password: Password in case of encrypted private file, defaults to '' :param encoding: Private file encoding, defaults to 'PEM' :param hash_alg: Hash for the signature, defaults to 'sha256' + :param mode: Optionally there could be specified mode of signature algorithm. + For example to switch EC signature to deterministic mode 'deterministic-rfc6979' must be used :raises SPSDKError: Invalid Private Key """ - self.file_path = file_path - self.private_key = crypto.load_private_key(file_path) + self.file_path = find_file(file_path=file_path, search_paths=search_paths) + self.private_key = crypto.load_private_key(self.file_path) + assert isinstance(self.private_key, crypto._PrivateKeyTuple) if hash_alg: hash_alg_name = hash_alg else: @@ -108,6 +138,7 @@ def __init__( raise SPSDKError( f"Unsupported private key by signature provider: {str(self.private_key)}" ) + self.mode = mode self.hash_alg = getattr(crypto.hashes, hash_alg_name.upper())() @property @@ -118,21 +149,41 @@ def signature_length(self) -> int: sig_len *= 2 return sig_len + def verify_public_key(self, public_key: bytes) -> bool: + """Verify if given public key matches private key.""" + crypto_public_key = crypto.loaders.load_public_key_from_data(public_key) + assert isinstance(crypto_public_key, crypto._PublicKeyTuple) + data = bytes() + if isinstance(crypto_public_key, crypto.RSAPublicKey): + signature = self._rsa_sign(data) + is_matching = crypto_backend().rsa_verify( + pub_key_mod=crypto_public_key.public_numbers().n, + pub_key_exp=crypto_public_key.public_numbers().e, + signature=signature, + data=data, + ) + return is_matching + else: # public_key can be only one of RSAPublicKey | EllipticCurvePublicKey type + signature = self._ecc_sign(data) + is_matching = crypto_backend().ecc_verify( + public_key=public_key, signature=signature, data=data + ) + return is_matching + def info(self) -> str: """Return basic into about the signature provider.""" - msg = "Plain File Signature Provider\n" - msg += f"Key path: {self.file_path}\n" + msg = super().info() + msg += f"\nKey path: {self.file_path}\n" return msg - def sign(self, data: bytes) -> Optional[bytes]: + def sign(self, data: bytes) -> bytes: """Return the signature for data.""" if isinstance(self.private_key, crypto.RSAPrivateKey): return self._rsa_sign(data) - if isinstance(self.private_key, crypto.EllipticCurvePrivateKey): + else: # self.private_key can be only one of RSAPrivateKey | RSAPublicKey type return self._ecc_sign(data) - return None - def _rsa_sign(self, data: bytes) -> Optional[bytes]: + def _rsa_sign(self, data: bytes) -> bytes: """Return RSA signature.""" assert isinstance(self.private_key, crypto.RSAPrivateKey) signature = self.private_key.sign( @@ -140,10 +191,53 @@ def _rsa_sign(self, data: bytes) -> Optional[bytes]: ) return signature - def _ecc_sign(self, data: bytes) -> Optional[bytes]: + def _ecc_sign(self, data: bytes) -> bytes: """Return ECC signature.""" assert isinstance(self.private_key, crypto.EllipticCurvePrivateKey) - signature = self.private_key.sign( - data=data, signature_algorithm=crypto.ec.ECDSA(self.hash_alg) - ) + if self.mode and self.mode == "deterministic-rfc6979": + private_key_bytes = self.private_key.private_bytes( + encoding=crypto.Encoding.PEM, + format=crypto.serialization.PrivateFormat.PKCS8, + encryption_algorithm=crypto.serialization.NoEncryption(), + ) + crypto_dome_key = ECC.import_key(private_key_bytes) + signature = internal_backend.ecc_sign(crypto_dome_key, data) + else: + signature = self.private_key.sign( + data=data, signature_algorithm=crypto.ec.ECDSA(self.hash_alg) + ) return signature + + +def get_signature_provider( + sp_cfg: Optional[str], local_file_key: Optional[str], **kwargs: Any +) -> SignatureProvider: + """Get the signature provider from configuration. + + :param sp_cfg: Configuration of signature provider. + :param local_file_key: Optional backward compatibility + option to specify just path to local private key. + :param kwargs: Additional parameters, that could be accepted by Signature providers. + :return: Signature Provider instance. + :raises SPSDKError: Invalid input configuration. + """ + if sp_cfg: + params: Dict[str, Union[str, List[str]]] = {} + params.update(SignatureProvider.convert_params(sp_cfg)) + for k, v in kwargs.items(): + if not k in params: + params[k] = v + signature_provider = SignatureProvider.create(params=params) + elif local_file_key: + signature_provider = PlainFileSP( + file_path=local_file_key, + search_paths=kwargs.get("search_paths"), + mode=kwargs.get("mode"), + ) + else: + raise SPSDKValueError("No signature provider configuration is provided") + + if not signature_provider: + raise SPSDKError(f"Cannot create signature provider from: {sp_cfg or local_file_key}") + + return signature_provider diff --git a/spsdk/dat/dac_packet.py b/spsdk/dat/dac_packet.py index 84d3cb48..f1b63976 100644 --- a/spsdk/dat/dac_packet.py +++ b/spsdk/dat/dac_packet.py @@ -83,7 +83,7 @@ def validate_against_dc(self, dc: DebugCredential) -> None: ) dc_rotkh = dc.get_rotkh() - if not all( + if dc_rotkh and not all( self.rotid_rkth_hash[x] == dc_rotkh[x] for x in range(len(self.rotid_rkth_hash)) ): raise SPSDKValueError( diff --git a/spsdk/dat/dar_packet.py b/spsdk/dat/dar_packet.py index 143bcfdb..04f40d9b 100644 --- a/spsdk/dat/dar_packet.py +++ b/spsdk/dat/dar_packet.py @@ -89,12 +89,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "DebugAuthenticateResponse": """ raise NotImplementedError("Derived class has to implement this method.") - @classmethod - def _get_class(cls, version: str, socc: int) -> "Type[DebugAuthenticateResponse]": + @staticmethod + def _get_class(version: str) -> "Type[DebugAuthenticateResponse]": """Get the right Debug Authentication Response class by the protocol version. :param version: DAT protocol version - :param socc: SOCC of used chip """ return _version_mapping[version] @@ -102,7 +101,6 @@ def _get_class(cls, version: str, socc: int) -> "Type[DebugAuthenticateResponse] def create( cls, version: str, - socc: int, dc: DebugCredential, auth_beacon: int, dac: DebugAuthenticationChallenge, @@ -111,14 +109,13 @@ def create( """Create a dar object out of input parameters. :param version: protocol version - :param socc: SoC Class :param dc: debug credential object :param auth_beacon: authentication beacon value :param dac: DebugAuthenticationChallenge object :param dck: string containing path to dck key :return: DAR object """ - klass = DebugAuthenticateResponse._get_class(version=version, socc=socc) + klass = DebugAuthenticateResponse._get_class(version=version) dar_obj = klass(debug_credential=dc, auth_beacon=auth_beacon, dac=dac, path_dck_private=dck) return dar_obj diff --git a/spsdk/dat/debug_credential.py b/spsdk/dat/debug_credential.py index be00975e..0b53b679 100644 --- a/spsdk/dat/debug_credential.py +++ b/spsdk/dat/debug_credential.py @@ -12,8 +12,8 @@ from typing import Any, List, Optional, Type from spsdk import SPSDKError, crypto -from spsdk.crypto import SignatureProvider from spsdk.crypto.loaders import extract_public_key +from spsdk.crypto.signature_provider import SignatureProvider, get_signature_provider from spsdk.dat.utils import ecc_key_to_bytes, ecc_public_numbers_to_bytes, rsa_key_to_bytes from spsdk.exceptions import SPSDKValueError from spsdk.image.ahab.ahab_container import SRKRecord, SRKTable @@ -36,7 +36,7 @@ class DebugCredential: 0x0001: "LPC550x, LPC55s0x, LPC551x, LPC55s1x, LPC552x, LPC55s2x, LPC55s6", 0x0004: "LPC55s3", 0x0005: "KW45xx/K32W1xx", - 0x5254049C: "i.MXRT1180", + 0x5254049C: "i.MXRT118x", } def __init__( @@ -214,6 +214,11 @@ def create_from_yaml_config( if "rotk" in yaml_config.keys(): yaml_config["rotk"] = find_file(yaml_config["rotk"], search_paths=search_paths) yaml_config["dck"] = find_file(yaml_config["dck"], search_paths=search_paths) + signature_provider = get_signature_provider( + sp_cfg=yaml_config.get("sign_provider"), + local_file_key=yaml_config.get("rotk"), + search_paths=search_paths, + ) dc_obj = klass( socc=yaml_config["socc"], uuid=bytes.fromhex(yaml_config["uuid"]), @@ -228,11 +233,7 @@ def create_from_yaml_config( rot_pub=klass._get_rot_pub( # pylint: disable=protected-access yaml_config["rot_id"], yaml_config["rot_meta"] ), - signature_provider=SignatureProvider.create( - # if the yaml_config doesn't contain 'sign_provider' assume file-type - yaml_config.get("sign_provider") - or f'type=file;file_path={yaml_config["rotk"]}' - ), + signature_provider=signature_provider, ) return dc_obj @@ -519,6 +520,8 @@ def get_rotkh(self) -> bytes: :return: RoTKH in bytes """ srk_records_num = self.rot_meta[0] >> 4 + if srk_records_num == 1: + return b"" key_length = 256 if ((len(self.rot_meta) - 4) // srk_records_num) == 32 else 384 return internal_backend.hash(data=self.rot_meta[4:], algorithm=f"sha{key_length}") diff --git a/spsdk/dat/debug_mailbox.py b/spsdk/dat/debug_mailbox.py index 1151aabe..dedd3548 100644 --- a/spsdk/dat/debug_mailbox.py +++ b/spsdk/dat/debug_mailbox.py @@ -31,15 +31,15 @@ def __init__( self, debug_probe: DebugProbe, reset: bool = True, - moredelay: float = 1.0, + moredelay: float = 0.0, op_timeout: int = 1000, ) -> None: """Initialize DebugMailbox object. :param debug_probe: Debug probe instance. :param reset: Do reset of debug mailbox during initialization, defaults to True. - :param moredelay: Time of extra delay after reset sequence, defaults to 1.0. - :param op_timeout: Atomic operation timeout, defaults to 4000. + :param moredelay: Time of extra delay after reset sequence, defaults to 0.0. + :param op_timeout: Atomic operation timeout, defaults to 1000. :raises SPSDKIOError: Various kind of vulnerabilities during connection to debug mailbox. """ # setup debug port / access point diff --git a/spsdk/dat/dm_commands.py b/spsdk/dat/dm_commands.py index e7b4111a..5c98c5e2 100644 --- a/spsdk/dat/dm_commands.py +++ b/spsdk/dat/dm_commands.py @@ -71,7 +71,7 @@ def run(self, params: Optional[List[int]] = None) -> List[Any]: ret = self.dm.spin_read(self.dm.registers["RETURN"]["address"]) logger.debug(f"-> spin_read: {format_value(ret, 32)}") - new_protocol = bool(ret >> 31) + new_protocol = bool(ret >> 31) # bit 31 is flag in new protocol version error_indication = bool(ret >> 20) and not bool(self.resplen) # solve the case that response is in legacy protocol and there is some # unwanted bits in none expected data. In this case return valid read data. @@ -112,7 +112,7 @@ def run_safe(self, raise_if_failure: bool = True, **args: Any) -> Optional[List[ return self.run(**args) except (SPSDKTimeoutError, TimeoutError) as error: if raise_if_failure: - raise error + raise SPSDKTimeoutError("Timeout occurred") from error logger.error(str(error)) return None diff --git a/spsdk/data/ahab/database.yml b/spsdk/data/ahab/database.yaml similarity index 84% rename from spsdk/data/ahab/database.yml rename to spsdk/data/ahab/database.yaml index e7112fd5..7d3b39f0 100644 --- a/spsdk/data/ahab/database.yml +++ b/spsdk/data/ahab/database.yaml @@ -1,9 +1,9 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause devices: - rt1180: + rt118x: revisions: a0: {} latest: a0 @@ -15,6 +15,8 @@ devices: oem_srkh_fuses_start: 0x80 oem_srkh_fuses_count: 8 oem_srkh_fuses_size: 4 + mx93: + device_alias: rt118x # Defaults attributes: diff --git a/spsdk/data/ahab/sch_ahab.yml b/spsdk/data/ahab/sch_ahab.yaml similarity index 97% rename from spsdk/data/ahab/sch_ahab.yml rename to spsdk/data/ahab/sch_ahab.yaml index 79e676b6..b33fd455 100644 --- a/spsdk/data/ahab/sch_ahab.yml +++ b/spsdk/data/ahab/sch_ahab.yaml @@ -1,3 +1,6 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause --- type: object required: @@ -13,8 +16,9 @@ properties: Family identifier including the chip revision. If revision is not present, latest revision is used as default. enum: - - rt1180 - template_value: rt1180 + - mx93 + - rt118x + template_value: rt118x revision: type: string title: MCU revision @@ -112,10 +116,8 @@ properties: type: string title: AHAB container signing key description: - Private key used for sign the container header. Header can be signed by SRK or - by image key that was signed by SRK. If an image key is used, it must be the - same algorithm and key size as the SRK. In both cases, the referenced SRK - must not have been revoked. + Private key used for sign the container header. Header can be signed by SRK. + The referenced SRK must not have been revoked. format: file template_value: my_signing_key.pem images: @@ -184,6 +186,7 @@ properties: title: Core ID description: Defines the core the image is dedicated for. enum: + - cortex-a55 - cortex-m33 - cortex-m7 template_value: cortex-m33 diff --git a/spsdk/data/ahab/sch_signed_msg.yml b/spsdk/data/ahab/sch_signed_msg.yaml similarity index 96% rename from spsdk/data/ahab/sch_signed_msg.yml rename to spsdk/data/ahab/sch_signed_msg.yaml index 569608ff..21b0e4ae 100644 --- a/spsdk/data/ahab/sch_signed_msg.yml +++ b/spsdk/data/ahab/sch_signed_msg.yaml @@ -1,3 +1,6 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause --- type: object required: @@ -19,8 +22,9 @@ properties: Family identifier including the chip revision. If revision is not present, latest revision is used as default. enum: - - rt1180 - template_value: rt1180 + - mx93 + - rt118x + template_value: rt118x revision: type: string title: MCU revision @@ -76,10 +80,8 @@ properties: type: string title: Signed Message container signing key description: - Private key used for sign the container header. Header can be signed by SRK or - by image key that was signed by SRK. If an image key is used, it must be the - same algorithm and key size as the SRK. In both cases, the referenced SRK - must not have been revoked. + Private key used for sign the container header. Header can be signed by SRK. + The referenced SRK must not have been revoked. format: file template_value: my_signing_key.pem diff --git a/spsdk/data/ifr/database.yaml b/spsdk/data/ifr/database.yaml index 41d5cb66..791885f5 100644 --- a/spsdk/data/ifr/database.yaml +++ b/spsdk/data/ifr/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: kw45xx: revisions: @@ -6,6 +10,8 @@ devices: a1: data_file: kw45xx_a1.xml latest: a1 + attributes: + address: 0x200_0000 k32w1xx: revisions: a0: @@ -13,3 +19,5 @@ devices: a1: data_file: k32w1xx_a1.xml latest: a1 + attributes: + address: 0x200_0000 diff --git a/spsdk/data/ifr/k32w1xx_a0.xml b/spsdk/data/ifr/k32w1xx_a0.xml index e011c4ec..3a2cc2b3 100644 --- a/spsdk/data/ifr/k32w1xx_a0.xml +++ b/spsdk/data/ifr/k32w1xx_a0.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/ifr/k32w1xx_a1.xml b/spsdk/data/ifr/k32w1xx_a1.xml index b3ccb29e..a3422038 100644 --- a/spsdk/data/ifr/k32w1xx_a1.xml +++ b/spsdk/data/ifr/k32w1xx_a1.xml @@ -1,17 +1,22 @@ + - + - + - + @@ -20,27 +25,27 @@ - - + + - - + + - - + + - - + + - - + + @@ -88,8 +93,8 @@ - - + + @@ -105,8 +110,8 @@ - - + + @@ -156,66 +161,30 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + @@ -230,8 +199,8 @@ - - + + diff --git a/spsdk/data/ifr/kw45xx_a0.xml b/spsdk/data/ifr/kw45xx_a0.xml index c01094f0..1101548a 100644 --- a/spsdk/data/ifr/kw45xx_a0.xml +++ b/spsdk/data/ifr/kw45xx_a0.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/ifr/kw45xx_a1.xml b/spsdk/data/ifr/kw45xx_a1.xml index df2acfda..a2f7ab62 100644 --- a/spsdk/data/ifr/kw45xx_a1.xml +++ b/spsdk/data/ifr/kw45xx_a1.xml @@ -1,17 +1,22 @@ + - + - + - + @@ -20,27 +25,27 @@ - - + + - - + + - - + + - - + + - - + + @@ -88,8 +93,8 @@ - - + + @@ -105,8 +110,8 @@ - - + + @@ -156,70 +161,34 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + @@ -246,8 +215,8 @@ - - + + diff --git a/spsdk/data/image/bootable_image/database.yml b/spsdk/data/image/bootable_image/database.yaml similarity index 60% rename from spsdk/data/image/bootable_image/database.yml rename to spsdk/data/image/bootable_image/database.yaml index d87400aa..b8c1e9a4 100644 --- a/spsdk/data/image/bootable_image/database.yml +++ b/spsdk/data/image/bootable_image/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -8,49 +8,60 @@ devices: mem_types: flexspi_nor: keyblob_offset: 0x0000 - keyblob_len: 256 fcb_offset: 0x0400 - fcb_len: 512 image_version_offset: 0x0600 - image_version_len: 4 keystore_offset: 0x0800 - keystore_len: 2048 application_offset: 0x1000 remap_align: 0x40000 # 256 KB rt6xx: device_alias: rt5xx - rt685: - device_alias: rt6xx lpc55s3x: attributes: mem_types: flexspi_nor: fcb_offset: 0x0400 - fcb_len: 512 image_version_offset: 0x0600 - image_version_len: 4 application_offset: 0x1000 remap_align: 0x40000 # 256 KB - rt105x: + rt101x: + attributes: + mem_types: + flexspi_nor: + keyblob_offset: 0x0000 + fcb_offset: 0x0400 + hab_container_offset: 0x1000 + rt102x: attributes: mem_types: flexspi_nor: fcb_offset: 0x0000 + bee_header_0_offset: 0x0400 + bee_header_1_offset: 0x0800 + hab_container_offset: 0x1000 + rt105x: + device_alias: rt102x rt106x: - device_alias: rt105x + device_alias: rt102x rt117x: attributes: mem_types: flexspi_nor: + keyblob_offset: 0x0000 fcb_offset: 0x0400 + keystore_offset: 0x0800 + hab_container_offset: 0x1000 + flexspi_nand: + hab_container_offset: 0x400 + semc_nand: + hab_container_offset: 0x400 + rt116x: + device_alias: rt117x rt118x: attributes: mem_types: flexspi_nor: keyblob_offset: 0x0000 - keyblob_len: 256 fcb_offset: 0x0400 - fcb_len: 512 xmcd_offset: 0x0800 ahab_container_offset: 0x1000 diff --git a/spsdk/data/image/bootable_image/sch_bimg.yml b/spsdk/data/image/bootable_image/sch_bimg.yaml similarity index 70% rename from spsdk/data/image/bootable_image/sch_bimg.yml rename to spsdk/data/image/bootable_image/sch_bimg.yaml index 0de67a7f..0ac60e5e 100644 --- a/spsdk/data/image/bootable_image/sch_bimg.yml +++ b/spsdk/data/image/bootable_image/sch_bimg.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -17,6 +17,7 @@ family_rev: title: Chip silicon revision description: If needed this could be used to specify silicon revision of device. template_value: latest + memory_type: type: string title: Memory type @@ -30,7 +31,7 @@ keyblob: format: optional_file title: Key Blob block path description: Key blob block path - template_value: key_blob.bin + template_value: keyblob.bin fcb: properties: @@ -57,9 +58,10 @@ keystore: format: optional_file title: Key Store block path description: Key store block path - template_value: key_store.bin + template_value: keystore.bin application: + required: [application] properties: application: type: string @@ -68,32 +70,23 @@ application: description: Application image path template_value: application.bin -bee: - properties: - bee: - type: string - title: BEE block path - format: optional_file - description: BEE block path - template_value: bee.bin - -ivt: +bee_header_0: properties: - ivt: + bee_header_0: type: string - title: IVT block path + title: BEE encryption header 0 format: optional_file - description: Image Vector Table block path - template_value: ivt.bin + description: BEE encryption header 0 path + template_value: bee_header_0.bin -bdi: +bee_header_1: properties: - bdi: + bee_header_1: type: string - title: BDI block path + title: BEE encryption header 1 format: optional_file - description: Boot Data Info block path - template_value: bdi.bin + description: BEE encryption header 1 path + template_value: bee_header_1.bin xmcd: properties: @@ -104,29 +97,23 @@ xmcd: description: External Memory Configuration Data Segment path template_value: xmcd.bin -dcd: +hab_container: + required: [hab_container] properties: - dcd: + hab_container: type: string - title: DCD block path + title: HAB container format: optional_file - description: Device Config Data block path - template_value: dcd.bin - -csf: - properties: - csf: - type: string - title: CSF block path - format: optional_file - description: Code Signing Data block path - template_value: csf.bin + description: HAB container path + template_value: hab_container.bin ahab_container: + required: [ahab_container] properties: ahab_container: type: string title: AHAB container format: optional_file description: AHAB container path - template_value: ahab_container.bin \ No newline at end of file + template_value: ahab_container.bin + diff --git a/spsdk/data/image/database.yml b/spsdk/data/image/database.yaml similarity index 99% rename from spsdk/data/image/database.yml rename to spsdk/data/image/database.yaml index 59f19d06..d1e0ccc6 100644 --- a/spsdk/data/image/database.yml +++ b/spsdk/data/image/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause diff --git a/spsdk/data/image/database_sb31.yaml b/spsdk/data/image/database_sb31.yaml new file mode 100644 index 00000000..c131cfad --- /dev/null +++ b/spsdk/data/image/database_sb31.yaml @@ -0,0 +1,15 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +devices: + lpc55s3x: + attributes: + supported_commands: [erase, load, execute, programFuses, programIFR, loadCMAC, copy, loadHashLocking, loadKeyBlob, configureMemory, fillMemory, checkFwVersion] + mcxn9xx: + device_alias: lpc55s3x + kw45xx: + attributes: + supported_commands: [erase, load, execute, programFuses, programIFR, loadCMAC, loadHashLocking, fillMemory, checkFwVersion] + k32w1xx: + device_alias: kw45xx diff --git a/spsdk/data/image/fcb/database.yml b/spsdk/data/image/fcb/database.yaml similarity index 63% rename from spsdk/data/image/fcb/database.yml rename to spsdk/data/image/fcb/database.yaml index 5178c762..1eb79ef7 100644 --- a/spsdk/data/image/fcb/database.yml +++ b/spsdk/data/image/fcb/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -15,6 +15,18 @@ devices: attributes: mem_types: flexspi_nor: rt6xx_flexspi_nor.xml + rt101x: + attributes: + mem_types: + flexspi_nor: rt101x_flexspi_nor.xml + rt102x: + attributes: + mem_types: + flexspi_nor: rt102x_flexspi_nor.xml + rt104x: + attributes: + mem_types: + flexspi_nor: rt104x_flexspi_nor.xml rt105x: attributes: mem_types: @@ -23,6 +35,10 @@ devices: attributes: mem_types: flexspi_nor: rt106x_flexspi_nor.xml + rt116x: + attributes: + mem_types: + flexspi_nor: rt116x_flexspi_nor.xml rt117x: attributes: mem_types: diff --git a/spsdk/data/image/fcb/lpc55s3x_flexspi_nor.xml b/spsdk/data/image/fcb/lpc55s3x_flexspi_nor.xml index aadcf083..fbc761ce 100644 --- a/spsdk/data/image/fcb/lpc55s3x_flexspi_nor.xml +++ b/spsdk/data/image/fcb/lpc55s3x_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/rt101x_flexspi_nor.xml b/spsdk/data/image/fcb/rt101x_flexspi_nor.xml new file mode 100644 index 00000000..df8d53e8 --- /dev/null +++ b/spsdk/data/image/fcb/rt101x_flexspi_nor.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/image/fcb/rt102x_flexspi_nor.xml b/spsdk/data/image/fcb/rt102x_flexspi_nor.xml new file mode 100644 index 00000000..df8d53e8 --- /dev/null +++ b/spsdk/data/image/fcb/rt102x_flexspi_nor.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/image/fcb/rt104x_flexspi_nor.xml b/spsdk/data/image/fcb/rt104x_flexspi_nor.xml new file mode 100644 index 00000000..df8d53e8 --- /dev/null +++ b/spsdk/data/image/fcb/rt104x_flexspi_nor.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/image/fcb/rt105x_flexspi_nor.xml b/spsdk/data/image/fcb/rt105x_flexspi_nor.xml index 06d6a4aa..df8d53e8 100644 --- a/spsdk/data/image/fcb/rt105x_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt105x_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/rt106x_flexspi_nor.xml b/spsdk/data/image/fcb/rt106x_flexspi_nor.xml index 06d6a4aa..df8d53e8 100644 --- a/spsdk/data/image/fcb/rt106x_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt106x_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/rt116x_flexspi_nor.xml b/spsdk/data/image/fcb/rt116x_flexspi_nor.xml new file mode 100644 index 00000000..df8d53e8 --- /dev/null +++ b/spsdk/data/image/fcb/rt116x_flexspi_nor.xml @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/image/fcb/rt117x_flexspi_nor.xml b/spsdk/data/image/fcb/rt117x_flexspi_nor.xml index 06d6a4aa..df8d53e8 100644 --- a/spsdk/data/image/fcb/rt117x_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt117x_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/rt118x_flexspi_nor.xml b/spsdk/data/image/fcb/rt118x_flexspi_nor.xml index 72a21b87..6dc82bfc 100644 --- a/spsdk/data/image/fcb/rt118x_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt118x_flexspi_nor.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/image/fcb/rt5xx_flexspi_nor.xml b/spsdk/data/image/fcb/rt5xx_flexspi_nor.xml index 562356ee..951bb168 100644 --- a/spsdk/data/image/fcb/rt5xx_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt5xx_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/rt6xx_flexspi_nor.xml b/spsdk/data/image/fcb/rt6xx_flexspi_nor.xml index 5791218e..18a49ad1 100644 --- a/spsdk/data/image/fcb/rt6xx_flexspi_nor.xml +++ b/spsdk/data/image/fcb/rt6xx_flexspi_nor.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/fcb/sch_fcb.yml b/spsdk/data/image/fcb/sch_fcb.yaml similarity index 100% rename from spsdk/data/image/fcb/sch_fcb.yml rename to spsdk/data/image/fcb/sch_fcb.yaml diff --git a/spsdk/data/image/sch_binary.yml b/spsdk/data/image/sch_binary.yaml similarity index 89% rename from spsdk/data/image/sch_binary.yml rename to spsdk/data/image/sch_binary.yaml index a0f1864e..968ab4b2 100644 --- a/spsdk/data/image/sch_binary.yml +++ b/spsdk/data/image/sch_binary.yaml @@ -1,4 +1,4 @@ -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -18,7 +18,9 @@ properties: pattern: title: Pattern defined as number or special values - description: The pattern that will be used to fill up gaps between defined regions. + description: The pattern that will be used to fill up gaps between defined regions. String or number or choose from predefined patterns - rand, zeros, ones, inc + type: [string, number] + template_value: zeros oneOf: - type: [string, number] title: Pattern defined as number @@ -59,7 +61,7 @@ properties: offset: type: [string, number] title: Offset of image - description: The offset of image to be merge on. + description: The offset of image to be merge on. The offset could be also negative - for example to 'erase' security bit from address. format: number template_value: 1024 - type: object @@ -89,7 +91,9 @@ properties: template_value: 1024 pattern: title: Pattern defined as number or special values - description: The pattern that will be used to fill up gaps between defined regions. + description: The pattern that will be used to fill up gaps between defined regions. String or number or choose from predefined patterns - rand, zeros, ones, inc + type: [string, number] + template_value: zeros oneOf: - type: [string, number] title: Pattern defined as number diff --git a/spsdk/data/image/sch_mbimg.yml b/spsdk/data/image/sch_mbimg.yaml similarity index 78% rename from spsdk/data/image/sch_mbimg.yml rename to spsdk/data/image/sch_mbimg.yaml index 063b08ac..7f0c3d63 100644 --- a/spsdk/data/image/sch_mbimg.yml +++ b/spsdk/data/image/sch_mbimg.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -39,7 +39,7 @@ tz: type: string title: TrustZone Customization file description: If not specified, but TrustZone is enabled(enableTrustZone) the default values are used. - template_value: my_tz_custom.yml + template_value: my_tz_custom.yaml load_addr: type: object @@ -75,7 +75,7 @@ image_subtype: sign_hash_len: type: object - title: Certificate V3.1 Settings + title: Image Manifest Settings properties: manifestSigningHashLength: type: number @@ -91,54 +91,76 @@ cert_prv_key: mainCertPrivateKeyFile: type: string title: Main Certificate private key - description: Main Certificate private key used to sign certificate + description: Main Certificate private key used to sign certificate. It can be replaced by signProvider key. template_value: my_prv_key.pem -use_isk: - type: object - title: Certificate V3.1 Settings - properties: - useIsk: - type: boolean - title: Use ISK for signature certification - description: Enable ISK type of signature certification - template_value: false - required: [useIsk] - -signing_cert_prv_key: - type: object - title: Certificate V3.1 Settings - properties: - signingCertificatePrivateKeyFile: - id: "#signingCertificatePrivateKeyFile" + signProvider: type: string - title: ISK Certificate private key - description: ISK Certificate private key used to sign certificate - template_value: isk_prv_key.pem + title: Signature Provider + description: Signature provider configuration in format 'type=;=;=". It can be replaced by mainCertPrivateKeyFile key. + template_value: type=file;file_path=my_prv_key.pem + + allOf: # Global all of group - this is main concatenation group for all sub rules + - oneOf: + - allOf: + - required: [mainCertPrivateKeyFile] + - not: + required: [signProvider] + - allOf: + - required: [signProvider] + - not: + required: [mainCertPrivateKeyFile] + - allOf: + - required: [mainRootCertPrivateKeyFile] + - not: + required: [mainCertPrivateKeyFile] signing_root_prv_key: type: object - title: Certificate V3.1 Settings + title: Root Keys Settings properties: mainRootCertPrivateKeyFile: id: "#mainRootCertPrivateKeyFile" type: string title: Main Certificate private key - description: Main Certificate private key used to sign certificate + description: Main Certificate private key used to sign certificate. It can be replaced by signProvider key. template_value: main_prv_key.pem +signature_provider: + type: object + title: Root Keys Settings + properties: + signProvider: + type: string + title: Signature Provider + description: Signature provider configuration in format 'type=;=;=". + template_value: type=file;file_path=my_prv_key.pem + signing_prv_key_lpc55s3x: type: object - title: Certificate V3.1 Settings + title: Root Keys Settings + + allOf: # Global all of group - this is main concatenation group for all sub rules + - oneOf: + - + required: [binaryCertificateBlock] + - + required: [useIsk] + if: + properties: + useIsk: + const: False + then: + oneOf: + - allOf: + - required: [mainRootCertPrivateKeyFile] + - not: + required: [signProvider] + - allOf: + - required: [signProvider] + - not: + required: [mainRootCertPrivateKeyFile] - if: - properties: - useIsk: - const: False - then: - required: [signingCertificatePrivateKeyFile] - else: - required: [mainRootCertPrivateKeyFile] hw_key: type: object @@ -267,19 +289,20 @@ elliptic_curves: - secp256r1 - secp384r1 template_value: secp256r1 - if: - properties: - attachSignDigest: - const: True - then: - if: - properties: - useIsk: - const: True - then: - required: [iskCertificateEllipticCurve] - else: - required: [rootCertificateEllipticCurve] + allOf: # Global all of group - this is main concatenation group for all sub rules + - if: + properties: + attachSignDigest: + const: True + then: + if: + properties: + useIsk: + const: True + then: + required: [iskCertificateEllipticCurve] + else: + required: [rootCertificateEllipticCurve] nxp_image: type: object diff --git a/spsdk/data/image/sch_sb3.yml b/spsdk/data/image/sch_sb3.yaml similarity index 98% rename from spsdk/data/image/sch_sb3.yml rename to spsdk/data/image/sch_sb3.yaml index 68a1e828..9e88ffed 100644 --- a/spsdk/data/image/sch_sb3.yml +++ b/spsdk/data/image/sch_sb3.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -10,10 +10,7 @@ sb3_family: type: string title: MCU family description: MCU family name. - enum: - - lpc55s3x - - kw45xx - - k32w1xx + enum: [] # Just placeholder to keep this record that will be fulfilled in runtime template_value: lpc55s3x required: [family] @@ -155,7 +152,8 @@ sb3_commands: authentication: type: string title: Authentication - description: Type of Image authentication [None, cmac, hashlocking]. If authentication is not used, just omit this option. + description: If authentication is not used, just omit this option or set 'none'. + enum: [none, cmac, hashlocking] template_value: cmac required: [address] diff --git a/spsdk/data/image/sch_tz.yml b/spsdk/data/image/sch_tz.yaml similarity index 83% rename from spsdk/data/image/sch_tz.yml rename to spsdk/data/image/sch_tz.yaml index 353a725a..bcafd1d7 100644 --- a/spsdk/data/image/sch_tz.yml +++ b/spsdk/data/image/sch_tz.yaml @@ -1,4 +1,4 @@ -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -9,17 +9,7 @@ tz_family_rev: type: string title: MCU family description: MCU family name. - enum: - - lpc55xx - - lpc55s0x - - lpc55s1x - - lpc55s3x - - lpc55s6x - - nhs52sxx - - rt5xx - - rt6xx - - kw45xx - - k32w1xx + enum: [] template_value: lpc55xx revision: diff --git a/spsdk/data/image/xmcd/database.yml b/spsdk/data/image/xmcd/database.yaml similarity index 100% rename from spsdk/data/image/xmcd/database.yml rename to spsdk/data/image/xmcd/database.yaml diff --git a/spsdk/data/image/xmcd/flexspi_ram_full.xml b/spsdk/data/image/xmcd/flexspi_ram_full.xml index 422a0fc8..799f09cb 100644 --- a/spsdk/data/image/xmcd/flexspi_ram_full.xml +++ b/spsdk/data/image/xmcd/flexspi_ram_full.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/xmcd/flexspi_ram_simplified.xml b/spsdk/data/image/xmcd/flexspi_ram_simplified.xml index 76d4f685..d28af33e 100644 --- a/spsdk/data/image/xmcd/flexspi_ram_simplified.xml +++ b/spsdk/data/image/xmcd/flexspi_ram_simplified.xml @@ -1,18 +1,32 @@ - + + - + - - + + - + + + + + + + + + + diff --git a/spsdk/data/image/xmcd/header.xml b/spsdk/data/image/xmcd/header.xml index 8e548e4f..48012f7a 100644 --- a/spsdk/data/image/xmcd/header.xml +++ b/spsdk/data/image/xmcd/header.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/xmcd/sch_xmcd.yml b/spsdk/data/image/xmcd/sch_xmcd.yaml similarity index 100% rename from spsdk/data/image/xmcd/sch_xmcd.yml rename to spsdk/data/image/xmcd/sch_xmcd.yaml diff --git a/spsdk/data/image/xmcd/semc_sdram_full.xml b/spsdk/data/image/xmcd/semc_sdram_full.xml index 141dff79..93a1ce17 100644 --- a/spsdk/data/image/xmcd/semc_sdram_full.xml +++ b/spsdk/data/image/xmcd/semc_sdram_full.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/image/xmcd/semc_sdram_simplified.xml b/spsdk/data/image/xmcd/semc_sdram_simplified.xml index f1ceb3a2..e37e95aa 100644 --- a/spsdk/data/image/xmcd/semc_sdram_simplified.xml +++ b/spsdk/data/image/xmcd/semc_sdram_simplified.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/nxpcertgen/certgen_config.yml b/spsdk/data/nxpcertgen/certgen_config.yaml similarity index 96% rename from spsdk/data/nxpcertgen/certgen_config.yml rename to spsdk/data/nxpcertgen/certgen_config.yaml index cf9bb113..67db72a4 100644 --- a/spsdk/data/nxpcertgen/certgen_config.yml +++ b/spsdk/data/nxpcertgen/certgen_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # This is template for configuration file used for generating certificates # ============================================== diff --git a/spsdk/data/nxpdebugmbox/template_config.yml b/spsdk/data/nxpdebugmbox/template_config.yaml similarity index 97% rename from spsdk/data/nxpdebugmbox/template_config.yml rename to spsdk/data/nxpdebugmbox/template_config.yaml index 8df72323..e242147d 100644 --- a/spsdk/data/nxpdebugmbox/template_config.yml +++ b/spsdk/data/nxpdebugmbox/template_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # DC Block structure # ============================================ # ============================================ @@ -44,7 +48,7 @@ # 0x0001: LPC550x, LPC55s0x, LPC551x, LPC55s1x, LPC552x, LPC55s2x, LPC55s6x # 0x0004: LPC55s3x # 0x0005: KW45xx/K32W1xx -# 0x5254049C: i.MXRT1180 +# 0x5254049C: i.MXRT118x socc: 0x0001 @@ -101,7 +105,7 @@ dck: dck.pub # There are two ways how sign the final DC data blob. # # 1. In case that you is available private pair for rot_meta with index rot_id just use first simple style -# to use it by rotk key. As a second way to do same is use sign_provider option with 'type=file'. +# to use it by rotk key. As a second way to do same is use sign_provider (or signProvider - both are accepted) option with 'type=file'. # # 2. For case that Debug Credential files are generated in untrusted environment (without access to RoT private keys), # there is option to use plugin (example how to create own plugin is in: ./SPSDK/examples/dat/hsm/). The plugin diff --git a/spsdk/data/nxpdevhsm/database.yaml b/spsdk/data/nxpdevhsm/database.yaml index a90b52ae..9fb71dca 100644 --- a/spsdk/data/nxpdevhsm/database.yaml +++ b/spsdk/data/nxpdevhsm/database.yaml @@ -1,3 +1,6 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause devices: lpc55s3x: attributes: diff --git a/spsdk/data/pfr/cfpa/database.yaml b/spsdk/data/pfr/cfpa/database.yaml index fd2e81e3..af7f67ab 100644 --- a/spsdk/data/pfr/cfpa/database.yaml +++ b/spsdk/data/pfr/cfpa/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55s6x: revisions: @@ -112,12 +116,6 @@ devices: address: 0x3_DC00 seal_start: CFPA_CRC32 seal_count: 1 - mandatory_computed_regs: 1 - computed_fields: - DCFG_CC_SOCU_NS_PIN: - INVERSE_VALUE: pfr_reg_inverse_high_half - DCFG_CC_SOCU_NS_DFLT: - INVERSE_VALUE: pfr_reg_inverse_high_half nhs52sxx: revisions: a1: diff --git a/spsdk/data/pfr/cfpa/lpc550x_a.xml b/spsdk/data/pfr/cfpa/lpc550x_a.xml index 6cabfcb4..d56c113e 100644 --- a/spsdk/data/pfr/cfpa/lpc550x_a.xml +++ b/spsdk/data/pfr/cfpa/lpc550x_a.xml @@ -1,4 +1,9 @@ + @@ -24,337 +29,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc551x_a.xml b/spsdk/data/pfr/cfpa/lpc551x_a.xml index 6cabfcb4..d56c113e 100644 --- a/spsdk/data/pfr/cfpa/lpc551x_a.xml +++ b/spsdk/data/pfr/cfpa/lpc551x_a.xml @@ -1,4 +1,9 @@ + @@ -24,337 +29,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc552x_1b.xml b/spsdk/data/pfr/cfpa/lpc552x_1b.xml index 6cabfcb4..d56c113e 100644 --- a/spsdk/data/pfr/cfpa/lpc552x_1b.xml +++ b/spsdk/data/pfr/cfpa/lpc552x_1b.xml @@ -1,4 +1,9 @@ + @@ -24,337 +29,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc553x_0a.xml b/spsdk/data/pfr/cfpa/lpc553x_0a.xml index 93f3299e..a49ea693 100644 --- a/spsdk/data/pfr/cfpa/lpc553x_0a.xml +++ b/spsdk/data/pfr/cfpa/lpc553x_0a.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/cfpa/lpc553x_1a.xml b/spsdk/data/pfr/cfpa/lpc553x_1a.xml index cacb7931..6b573773 100644 --- a/spsdk/data/pfr/cfpa/lpc553x_1a.xml +++ b/spsdk/data/pfr/cfpa/lpc553x_1a.xml @@ -1,4 +1,9 @@ + @@ -126,82 +131,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spsdk/data/pfr/cfpa/lpc55s0x_a.xml b/spsdk/data/pfr/cfpa/lpc55s0x_a.xml index 2c2b4df9..b6297d10 100644 --- a/spsdk/data/pfr/cfpa/lpc55s0x_a.xml +++ b/spsdk/data/pfr/cfpa/lpc55s0x_a.xml @@ -1,4 +1,9 @@ + @@ -34,31 +39,31 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -71,91 +76,91 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -747,337 +752,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc55s1x_a.xml b/spsdk/data/pfr/cfpa/lpc55s1x_a.xml index 2c2b4df9..b6297d10 100644 --- a/spsdk/data/pfr/cfpa/lpc55s1x_a.xml +++ b/spsdk/data/pfr/cfpa/lpc55s1x_a.xml @@ -1,4 +1,9 @@ + @@ -34,31 +39,31 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -71,91 +76,91 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -747,337 +752,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc55s2x_1b.xml b/spsdk/data/pfr/cfpa/lpc55s2x_1b.xml index fd70ba09..ca3d9b9b 100644 --- a/spsdk/data/pfr/cfpa/lpc55s2x_1b.xml +++ b/spsdk/data/pfr/cfpa/lpc55s2x_1b.xml @@ -1,4 +1,9 @@ + @@ -34,31 +39,31 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -71,113 +76,75 @@ - + - - + + - - - - - - - - - - - - + + + - - - - - - - + + + - - + + - - - - - - - - + + - + - - + + - + - - + + - - - - - - - - - - - - + + + - - - - - - - + + + - - + + - - - - - - - - + + - + @@ -767,337 +734,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml b/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml index bfa321b0..5d4c4045 100644 --- a/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml +++ b/spsdk/data/pfr/cfpa/lpc55s3x_0a.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/cfpa/lpc55s3x_1a.xml b/spsdk/data/pfr/cfpa/lpc55s3x_1a.xml index 73655294..334f15b3 100644 --- a/spsdk/data/pfr/cfpa/lpc55s3x_1a.xml +++ b/spsdk/data/pfr/cfpa/lpc55s3x_1a.xml @@ -1,4 +1,9 @@ + @@ -124,37 +129,37 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + @@ -167,101 +172,91 @@ - + - - + + - - + + - - + + - - - - - - - - + + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - - - - - - - + + + - - + + - - + + - - + + @@ -284,21 +279,21 @@ - - - - + + + + - - - - - - - - + + + + + + + + diff --git a/spsdk/data/pfr/cfpa/lpc55s6x_1b.xml b/spsdk/data/pfr/cfpa/lpc55s6x_1b.xml index fd70ba09..242190c7 100644 --- a/spsdk/data/pfr/cfpa/lpc55s6x_1b.xml +++ b/spsdk/data/pfr/cfpa/lpc55s6x_1b.xml @@ -1,4 +1,9 @@ + @@ -34,31 +39,31 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -71,111 +76,111 @@ - + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + @@ -767,337 +772,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cfpa/nhs52s04.xml b/spsdk/data/pfr/cfpa/nhs52s04.xml index 2fcd0716..70446f1f 100644 --- a/spsdk/data/pfr/cfpa/nhs52s04.xml +++ b/spsdk/data/pfr/cfpa/nhs52s04.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/cmpa/database.yaml b/spsdk/data/pfr/cmpa/database.yaml index 5db3d799..2bc4b78e 100644 --- a/spsdk/data/pfr/cmpa/database.yaml +++ b/spsdk/data/pfr/cmpa/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55s6x: revisions: diff --git a/spsdk/data/pfr/cmpa/lpc550x_a.xml b/spsdk/data/pfr/cmpa/lpc550x_a.xml index b0999b11..0babe634 100644 --- a/spsdk/data/pfr/cmpa/lpc550x_a.xml +++ b/spsdk/data/pfr/cmpa/lpc550x_a.xml @@ -1,20 +1,25 @@ + - - - - - + + + + + - - + + @@ -28,71 +33,73 @@ - + - - + + - - + + - - + + - - + + - - + + + + - + - - + + - - + + - - + + - - + + - - + + @@ -103,8 +110,8 @@ - - + + @@ -121,8 +128,8 @@ - - + + @@ -147,337 +154,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc551x_a.xml b/spsdk/data/pfr/cmpa/lpc551x_a.xml index af57ea63..0e064ff1 100644 --- a/spsdk/data/pfr/cmpa/lpc551x_a.xml +++ b/spsdk/data/pfr/cmpa/lpc551x_a.xml @@ -1,28 +1,33 @@ + - - - - - - + + + + + + - - + + - - - - + + + + @@ -51,68 +56,68 @@ - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + @@ -123,8 +128,8 @@ - - + + @@ -141,8 +146,8 @@ - - + + @@ -167,337 +172,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc552x_1b.xml b/spsdk/data/pfr/cmpa/lpc552x_1b.xml index c11f96be..20cd6543 100644 --- a/spsdk/data/pfr/cmpa/lpc552x_1b.xml +++ b/spsdk/data/pfr/cmpa/lpc552x_1b.xml @@ -1,29 +1,34 @@ + - - - - - - + + + + + + - - - + + + - - - - + + + + @@ -52,434 +57,405 @@ - + - - + + - - + + - - - - - - - + + + - - + + - - - - - - - - + + - + - + - - + + - - - - - - - - - - - - + + + - - - - - - - + + + - - + + - - - - - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc553x_0a.xml b/spsdk/data/pfr/cmpa/lpc553x_0a.xml index 02beaeac..2f2e177f 100644 --- a/spsdk/data/pfr/cmpa/lpc553x_0a.xml +++ b/spsdk/data/pfr/cmpa/lpc553x_0a.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/cmpa/lpc553x_1a.xml b/spsdk/data/pfr/cmpa/lpc553x_1a.xml index 75f014e4..131e7cce 100644 --- a/spsdk/data/pfr/cmpa/lpc553x_1a.xml +++ b/spsdk/data/pfr/cmpa/lpc553x_1a.xml @@ -1,36 +1,41 @@ + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + @@ -53,34 +58,34 @@ - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -102,79 +107,71 @@ - + - - + + - - - - - - - - + + + - - + + - - + + - - + + + + - + - - + + - - - - - - - - + + + - - + + - - + + - - + + @@ -185,8 +182,8 @@ - - + + @@ -203,8 +200,8 @@ - - + + @@ -233,49 +230,49 @@ - - + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - + + @@ -285,34 +282,34 @@ - - - - + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + @@ -326,22 +323,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -369,361 +366,361 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc55s0x_a.xml b/spsdk/data/pfr/cmpa/lpc55s0x_a.xml index 535b19f2..d6707198 100644 --- a/spsdk/data/pfr/cmpa/lpc55s0x_a.xml +++ b/spsdk/data/pfr/cmpa/lpc55s0x_a.xml @@ -1,20 +1,25 @@ + - - - - - + + + + + - - + + @@ -34,91 +39,91 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -136,95 +141,95 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -241,39 +246,39 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -299,8 +304,8 @@ - - + + @@ -317,8 +322,8 @@ - - + + @@ -391,337 +396,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc55s1x_a.xml b/spsdk/data/pfr/cmpa/lpc55s1x_a.xml index 226f4916..5b8c7baa 100644 --- a/spsdk/data/pfr/cmpa/lpc55s1x_a.xml +++ b/spsdk/data/pfr/cmpa/lpc55s1x_a.xml @@ -1,28 +1,33 @@ + - - - - - - + + + + + + - - + + - - - - + + + + @@ -51,91 +56,91 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -153,95 +158,95 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -258,39 +263,39 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -316,8 +321,8 @@ - - + + @@ -334,8 +339,8 @@ - - + + @@ -408,337 +413,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc55s2x_1b.xml b/spsdk/data/pfr/cmpa/lpc55s2x_1b.xml index 05547255..7d8040c7 100644 --- a/spsdk/data/pfr/cmpa/lpc55s2x_1b.xml +++ b/spsdk/data/pfr/cmpa/lpc55s2x_1b.xml @@ -1,29 +1,34 @@ + - - - - - - + + + + + + - - - + + + - - - - + + + + @@ -52,113 +57,75 @@ - + - - + + - - - - - - - - - - - - + + + - - - - - - - + + + - - + + - - - - - - - - + + - + - - + + - + - - + + - - - - - - - - - - - - + + + - - - - - - - + + + - - + + - - - - - - - - + + - + @@ -174,52 +141,52 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -227,10 +194,10 @@ - - - - + + + + @@ -247,39 +214,39 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -305,8 +272,8 @@ - - + + @@ -323,8 +290,8 @@ - - + + @@ -385,337 +352,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml b/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml index 6adfbfae..3395dce6 100644 --- a/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml +++ b/spsdk/data/pfr/cmpa/lpc55s3x_0a.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/cmpa/lpc55s3x_1a.xml b/spsdk/data/pfr/cmpa/lpc55s3x_1a.xml index 8187517b..3f072e73 100644 --- a/spsdk/data/pfr/cmpa/lpc55s3x_1a.xml +++ b/spsdk/data/pfr/cmpa/lpc55s3x_1a.xml @@ -1,44 +1,49 @@ + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - - - - + + + + @@ -60,34 +65,34 @@ - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -109,101 +114,91 @@ - + - - + + - - + + - - + + - - - - - - - - + + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - - - - - - - + + + - - + + - - + + - - + + @@ -219,105 +214,105 @@ - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + @@ -325,46 +320,46 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -390,8 +385,8 @@ - - + + @@ -408,8 +403,8 @@ - - + + @@ -456,14 +451,14 @@ - - - - - - - - + + + + + + + + @@ -552,49 +547,49 @@ - - + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - + + @@ -604,34 +599,34 @@ - - - - + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + @@ -645,22 +640,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -668,10 +663,10 @@ - - - - + + + + @@ -689,10 +684,10 @@ - - - - + + + + @@ -711,10 +706,10 @@ - - - - + + + + @@ -732,10 +727,10 @@ - - - - + + + + @@ -773,361 +768,361 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/lpc55s6x_1b.xml b/spsdk/data/pfr/cmpa/lpc55s6x_1b.xml index 05547255..337725de 100644 --- a/spsdk/data/pfr/cmpa/lpc55s6x_1b.xml +++ b/spsdk/data/pfr/cmpa/lpc55s6x_1b.xml @@ -1,29 +1,34 @@ + - - - - - - + + + + + + - - - + + + - - - - + + + + @@ -52,111 +57,111 @@ - + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + @@ -174,52 +179,52 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -227,10 +232,10 @@ - - - - + + + + @@ -247,39 +252,39 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -305,8 +310,8 @@ - - + + @@ -323,8 +328,8 @@ - - + + @@ -385,337 +390,337 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/spsdk/data/pfr/cmpa/nhs52s04.xml b/spsdk/data/pfr/cmpa/nhs52s04.xml index 4ad60057..fb9ae9b2 100644 --- a/spsdk/data/pfr/cmpa/nhs52s04.xml +++ b/spsdk/data/pfr/cmpa/nhs52s04.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/pfr/pfrc/database.yaml b/spsdk/data/pfr/pfrc/database.yaml index 7a61eec6..820e0d29 100644 --- a/spsdk/data/pfr/pfrc/database.yaml +++ b/spsdk/data/pfr/pfrc/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause diff --git a/spsdk/data/pfr/pfrc/rules_common.yaml b/spsdk/data/pfr/pfrc/rules_common.yaml index 39ffc359..cf4c9dec 100644 --- a/spsdk/data/pfr/pfrc/rules_common.yaml +++ b/spsdk/data/pfr/pfrc/rules_common.yaml @@ -1,15 +1,17 @@ -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause - req_id: "1.1" - desc: Never write any non-zero configuration into DCFG_CC_SOCU_NS_PIN before DCFG_CC_SOCU_PIN + desc: + Never write any non-zero configuration into DCFG_CC_SOCU_NS_PIN before DCFG_CC_SOCU_PIN contains any valid (non-zero) configuration. msg: The CMPA.DCFG_CC_SOCU_PIN[0:9] must be set in case the CFPA.DCFG_CC_SOCU_NS_PIN[0:9] is set. cond: any(True if (((CFPA.DCFG_CC_SOCU_NS_PIN >> index) & 1) and not ((CMPA.DCFG_CC_SOCU_PIN >> index) & 1)) else False for index in range(0, int(10))) - req_id: "1.2" - desc: Never write any non-zero configuration into DCFG_CC_SOCU_NS_DFLT before DCFG_CC_SOCU_DFLT + desc: + Never write any non-zero configuration into DCFG_CC_SOCU_NS_DFLT before DCFG_CC_SOCU_DFLT contains any valid (non-zero) configuration. msg: The CMPA.DCFG_CC_SOCU_DFLT[0:9] must be set in case the CFPA.DCFG_CC_SOCU_NS_DFLT[0:9] is set. cond: any(True if (((CFPA.DCFG_CC_SOCU_NS_DFLT >> index) & 1) and not ((CMPA.DCFG_CC_SOCU_DFLT >> index) & 1)) else False for index in range(0, int(10))) @@ -43,19 +45,22 @@ cond: CFPA.DCFG_CC_SOCU_NS_DFLT != 0 and (~(CFPA.DCFG_CC_SOCU_NS_DFLT>>int(16)) & 0xFFFF) != (CFPA.DCFG_CC_SOCU_NS_DFLT & 0xFFFF) - req_id: "1.7" - desc: Do not write invalid PIN/DFLT configuration in CMPA area. Setting PIN bit + desc: + Do not write invalid PIN/DFLT configuration in CMPA area. Setting PIN bit to 0 and DFLT bit to 1 for given feature is not allowed msg: Invalid bit combination. If CMPA.DCFG_CC_SOCU_PIN[0:9] is 0, CMPA.DCFG_CC_SOCU_DFLT[0:9] can't be set to 1! cond: any(True if (not ((CMPA.DCFG_CC_SOCU_PIN >> index) & 1) and ((CMPA.DCFG_CC_SOCU_DFLT >> index) & 1)) else False for index in range(0, int(10))) - req_id: "1.8" - desc: Do not write invalid PIN/DFLT configuration in CFPA area. Setting PIN bit + desc: + Do not write invalid PIN/DFLT configuration in CFPA area. Setting PIN bit to 0 and DFLT bit to 1 for given feature is not allowed msg: Invalid bit combination. If CFPA.DCFG_CC_SOCU_NS_PIN[0:9] is 0, CFPA.DCFG_CC_SOCU_NS_DFLT[0:9] can't be set to 1! cond: any(True if (not ((CFPA.DCFG_CC_SOCU_NS_PIN >> index) & 1) and ((CFPA.DCFG_CC_SOCU_NS_DFLT >> index) & 1)) else False for index in range(0, int(10))) - req_id: "2.1" - desc: This CMPA_PROG_IN_PROGRESS must be always 0x00000000. Only ROM bootloader + 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/pfrc/rules_lpc55s3x.yaml b/spsdk/data/pfr/pfrc/rules_lpc55s3x.yaml index 9c3afb65..e57c0c96 100644 --- a/spsdk/data/pfr/pfrc/rules_lpc55s3x.yaml +++ b/spsdk/data/pfr/pfrc/rules_lpc55s3x.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -14,4 +14,4 @@ desc: Inverse value (upper 16 bits) of VENDOR_USAGE must be always valid. msg: Inverse values are generated automatically based on configuration. - cond: CFPA.VENDOR_USAGE != 0 and (~(CFPA.VENDOR_USAGE>>int(16)) & 0xFFFF) != (CFPA.VENDOR_USAGE & 0xFFFF) \ No newline at end of file + cond: CFPA.VENDOR_USAGE != 0 and (~(CFPA.VENDOR_USAGE>>int(16)) & 0xFFFF) != (CFPA.VENDOR_USAGE & 0xFFFF) diff --git a/spsdk/data/pfr/pfrc/rules_lpc55xx.yaml b/spsdk/data/pfr/pfrc/rules_lpc55xx.yaml index 1bde0536..a4c77cab 100644 --- a/spsdk/data/pfr/pfrc/rules_lpc55xx.yaml +++ b/spsdk/data/pfr/pfrc/rules_lpc55xx.yaml @@ -1,9 +1,10 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause - req_id: "lpc55xx_4.1" - desc: By default (blank device), the RoTKx_EN values are set to 0b00 which means + desc: + By default (blank device), the RoTKx_EN values are set to 0b00 which means invalid. When secure boot is enabled (SEC_BOOT_EN != 0) there must be at least one RoTKx_EN which is set to 0b01 (enabled) to allow device to boot successfully. @@ -14,4 +15,4 @@ desc: Inverse value (upper 16 bits) of VENDOR_USAGE must be always valid. msg: Inverse values are generated automatically based on configuration. - cond: CFPA.VENDOR_USAGE != 0 and (~(CFPA.VENDOR_USAGE>>int(16)) & 0xFFFF) != (CFPA.VENDOR_USAGE & 0xFFFF) \ No newline at end of file + cond: CFPA.VENDOR_USAGE != 0 and (~(CFPA.VENDOR_USAGE>>int(16)) & 0xFFFF) != (CFPA.VENDOR_USAGE & 0xFFFF) diff --git a/spsdk/data/shadowregs/database.yaml b/spsdk/data/shadowregs/database.yaml index e077d58b..ff1c5e84 100644 --- a/spsdk/data/shadowregs/database.yaml +++ b/spsdk/data/shadowregs/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: rt5xx: revisions: diff --git a/spsdk/data/shadowregs/imxrt595_b0.xml b/spsdk/data/shadowregs/imxrt595_b0.xml index 85313b1b..0d758d2e 100644 --- a/spsdk/data/shadowregs/imxrt595_b0.xml +++ b/spsdk/data/shadowregs/imxrt595_b0.xml @@ -1,4 +1,9 @@ - + + diff --git a/spsdk/data/shadowregs/imxrt685_b0.xml b/spsdk/data/shadowregs/imxrt685_b0.xml index 2b88c923..18392ee7 100644 --- a/spsdk/data/shadowregs/imxrt685_b0.xml +++ b/spsdk/data/shadowregs/imxrt685_b0.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/tp/database.yaml b/spsdk/data/tp/database.yaml index 2c49a311..f136cbd9 100644 --- a/spsdk/data/tp/database.yaml +++ b/spsdk/data/tp/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55s6x: attributes: diff --git a/spsdk/data/tp/sch_tp.yaml b/spsdk/data/tp/sch_tp.yaml index 3497e453..12906044 100644 --- a/spsdk/data/tp/sch_tp.yaml +++ b/spsdk/data/tp/sch_tp.yaml @@ -1,4 +1,4 @@ -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause diff --git a/spsdk/data/tp/tpconfig_cfg_template.yml b/spsdk/data/tp/tpconfig_cfg_template.yaml similarity index 95% rename from spsdk/data/tp/tpconfig_cfg_template.yml rename to spsdk/data/tp/tpconfig_cfg_template.yaml index d4403f35..ad9cd862 100644 --- a/spsdk/data/tp/tpconfig_cfg_template.yml +++ b/spsdk/data/tp/tpconfig_cfg_template.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # The template configuration file for TPCONFIG application version: 3 diff --git a/spsdk/data/tp/tphost_cfg_template.yml b/spsdk/data/tp/tphost_cfg_template.yaml similarity index 94% rename from spsdk/data/tp/tphost_cfg_template.yml rename to spsdk/data/tp/tphost_cfg_template.yaml index ed93364a..af9e75df 100644 --- a/spsdk/data/tp/tphost_cfg_template.yml +++ b/spsdk/data/tp/tphost_cfg_template.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # The template configuration file for TPHOST application version: 2 diff --git a/spsdk/data/tz_presets/database.yaml b/spsdk/data/tz_presets/database.yaml index 1a8713a4..6d10ef64 100644 --- a/spsdk/data/tz_presets/database.yaml +++ b/spsdk/data/tz_presets/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55xx: revisions: diff --git a/spsdk/data/tz_presets/kw45xx_a0.yaml b/spsdk/data/tz_presets/kw45xx_a0.yaml index 61dfd757..dba2ee26 100644 --- a/spsdk/data/tz_presets/kw45xx_a0.yaml +++ b/spsdk/data/tz_presets/kw45xx_a0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Start of block (tzm_magic): "0x4d2d5a54" TZM info which data are initialized (tzm_control): "0x0" CM33 Secure vector table address (cm33_vtor_addr): "0x0" diff --git a/spsdk/data/tz_presets/lpc55s0x.yaml b/spsdk/data/tz_presets/lpc55s0x.yaml index 38ecdc95..cbe50ab7 100644 --- a/spsdk/data/tz_presets/lpc55s0x.yaml +++ b/spsdk/data/tz_presets/lpc55s0x.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/lpc55s1x.yaml b/spsdk/data/tz_presets/lpc55s1x.yaml index 1244f8f2..df75f9cd 100644 --- a/spsdk/data/tz_presets/lpc55s1x.yaml +++ b/spsdk/data/tz_presets/lpc55s1x.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/lpc55s3x_a0.yaml b/spsdk/data/tz_presets/lpc55s3x_a0.yaml index 64f29e55..561b52d8 100644 --- a/spsdk/data/tz_presets/lpc55s3x_a0.yaml +++ b/spsdk/data/tz_presets/lpc55s3x_a0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Start of block (tzm_magic): "0x4d2d5a54" Secure vector table address (cm33_vtor_addr): "0x0" Non-secure vector table address (cm33_vtor_ns_addr): "0x0" diff --git a/spsdk/data/tz_presets/lpc55s3x_a1.yaml b/spsdk/data/tz_presets/lpc55s3x_a1.yaml index 7f2e3785..ed189d5c 100644 --- a/spsdk/data/tz_presets/lpc55s3x_a1.yaml +++ b/spsdk/data/tz_presets/lpc55s3x_a1.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Start of block (tzm_magic): "0x4d2d5a54" Secure vector table address (cm33_vtor_addr): "0x0" Non-secure vector table address (cm33_vtor_ns_addr): "0x0" diff --git a/spsdk/data/tz_presets/lpc55xx.yaml b/spsdk/data/tz_presets/lpc55xx.yaml index a0641048..5e023e6e 100644 --- a/spsdk/data/tz_presets/lpc55xx.yaml +++ b/spsdk/data/tz_presets/lpc55xx.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/lpc55xx_a0.yaml b/spsdk/data/tz_presets/lpc55xx_a0.yaml index 509063bb..a6db94c5 100644 --- a/spsdk/data/tz_presets/lpc55xx_a0.yaml +++ b/spsdk/data/tz_presets/lpc55xx_a0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/lpc55xx_a1.yaml b/spsdk/data/tz_presets/lpc55xx_a1.yaml index a0641048..5e023e6e 100644 --- a/spsdk/data/tz_presets/lpc55xx_a1.yaml +++ b/spsdk/data/tz_presets/lpc55xx_a1.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/nhs52s04.yaml b/spsdk/data/tz_presets/nhs52s04.yaml index 938b0902..b0a8c18f 100644 --- a/spsdk/data/tz_presets/nhs52s04.yaml +++ b/spsdk/data/tz_presets/nhs52s04.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + CM33 Secure vector table address (cm33_vtor_addr): "0x0" CM33 Non-secure vector table address (cm33_vtor_ns_addr): "0x0" CM33 Interrupt target non-secure register 0 (cm33_nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/rt5xx.yaml b/spsdk/data/tz_presets/rt5xx.yaml index ce4270eb..4f424a0b 100644 --- a/spsdk/data/tz_presets/rt5xx.yaml +++ b/spsdk/data/tz_presets/rt5xx.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Secure vector table address (vtor_addr): "0x00000000" Non-secure vector table address (vtor_ns_addr): "0x00000000" Interrupt target non-secure register 0 (nvic_itns0): "0x00000000" diff --git a/spsdk/data/tz_presets/rt5xx_a0.yaml b/spsdk/data/tz_presets/rt5xx_a0.yaml index fd284069..305b4b60 100644 --- a/spsdk/data/tz_presets/rt5xx_a0.yaml +++ b/spsdk/data/tz_presets/rt5xx_a0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Secure vector table address (vtor_addr): "0x0" Non-secure vector table address (vtor_ns_addr): "0x0" Interrupt target non-secure register 0 (nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/rt6xx.yaml b/spsdk/data/tz_presets/rt6xx.yaml index de0012d3..cea525e1 100644 --- a/spsdk/data/tz_presets/rt6xx.yaml +++ b/spsdk/data/tz_presets/rt6xx.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Secure vector table address (vtor_addr): "0x00000000" Non-secure vector table address (vtor_ns_addr): "0x00000000" Interrupt target non-secure register 0 (nvic_itns0): "0x00000000" diff --git a/spsdk/data/tz_presets/rt6xx_a0.yaml b/spsdk/data/tz_presets/rt6xx_a0.yaml index f52a4f5e..f468b5aa 100644 --- a/spsdk/data/tz_presets/rt6xx_a0.yaml +++ b/spsdk/data/tz_presets/rt6xx_a0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Secure vector table address (vtor_addr): "0x0" Non-secure vector table address (vtor_ns_addr): "0x0" Interrupt target non-secure register 0 (nvic_itns0): "0x0" diff --git a/spsdk/data/tz_presets/rt6xx_b0.yaml b/spsdk/data/tz_presets/rt6xx_b0.yaml index d27ce11a..0945dfb3 100644 --- a/spsdk/data/tz_presets/rt6xx_b0.yaml +++ b/spsdk/data/tz_presets/rt6xx_b0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Secure vector table address (vtor_addr): "0x0" Non-secure vector table address (vtor_ns_addr): "0x0" Interrupt target non-secure register 0 (nvic_itns0): "0x0" diff --git a/spsdk/data/utils/bee/database.yaml b/spsdk/data/utils/bee/database.yaml index be3ec294..9340abf2 100644 --- a/spsdk/data/utils/bee/database.yaml +++ b/spsdk/data/utils/bee/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause diff --git a/spsdk/data/utils/bee/sch_bee.yml b/spsdk/data/utils/bee/sch_bee.yaml similarity index 100% rename from spsdk/data/utils/bee/sch_bee.yml rename to spsdk/data/utils/bee/sch_bee.yaml diff --git a/spsdk/data/utils/iee/database.yml b/spsdk/data/utils/iee/database.yaml similarity index 68% rename from spsdk/data/utils/iee/database.yml rename to spsdk/data/utils/iee/database.yaml index ea6c2fd0..370d5dbd 100644 --- a/spsdk/data/utils/iee/database.yml +++ b/spsdk/data/utils/iee/database.yaml @@ -1,14 +1,15 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause devices: - rt1170: + rt117x: latest: a0 attributes: reg_fuses: fuses_rt117x.xml sb_21_supported: False has_kek_fuses: True + additional_template: ["ibkek", "key_blobs"] grouped_registers: - name: USER_KEY1 @@ -22,6 +23,17 @@ devices: config_as_hexstring: true description: USER KEY 2 (IBKEK). + rt116x: + device_alias: rt117x + + rt118x: + latest: a0 + attributes: + generate_keyblob: False + sb_21_supported: False + has_kek_fuses: False + additional_template: ["master_id", "key_blobs_rt118x"] + # default values for all devices attributes: key_blob_rec_size: 96 @@ -31,3 +43,4 @@ attributes: has_kek_fuses: False additional_template: [] additional_template_text: "" + generate_keyblob: True diff --git a/spsdk/data/utils/iee/fuses_rt117x.xml b/spsdk/data/utils/iee/fuses_rt117x.xml index e257deda..b323196d 100644 --- a/spsdk/data/utils/iee/fuses_rt117x.xml +++ b/spsdk/data/utils/iee/fuses_rt117x.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/utils/iee/sch_iee.yml b/spsdk/data/utils/iee/sch_iee.yaml similarity index 72% rename from spsdk/data/utils/iee/sch_iee.yml rename to spsdk/data/utils/iee/sch_iee.yaml index 7199acd1..30b69541 100644 --- a/spsdk/data/utils/iee/sch_iee.yml +++ b/spsdk/data/utils/iee/sch_iee.yaml @@ -50,22 +50,8 @@ iee_output: iee: type: object title: IEE Settings - required: [key_blobs, ibkek1, ibkek2, keyblob_address] + required: [keyblob_address] properties: - ibkek1: - type: [string, number] - title: IBKEK1 AES-XTS 256-bit key - description: IBKEK1 AES-XTS key for keyblob encryption - format: number - template_value: "0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" - - ibkek2: - type: [string, number] - title: IBKEK2 AES-XTS 256-bit key - description: IBKEK2 AES-XTS key for keyblob encryption - format: number - template_value: "0x202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F" - keyblob_address: type: [string, number] title: Base address of the IEE keyblob @@ -95,8 +81,31 @@ iee: format: number template_value: "0x03001000" - key_blobs: - title: List of Key Blobs used by IEE +ibkek: + type: object + title: Keyblob Encryption Key + required: [ibkek1, ibkek2] + properties: + ibkek1: + type: [string, number] + title: IBKEK1 AES-XTS 256-bit key + description: IBKEK1 AES-XTS key for keyblob encryption + format: number + template_value: "0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" + + ibkek2: + type: [string, number] + title: IBKEK2 AES-XTS 256-bit key + description: IBKEK2 AES-XTS key for keyblob encryption + format: number + template_value: "0x202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F" + +key_blobs: + type: object + title: List of Key Blobs used by IEE + required: [key_blobs] + properties: + key_blob: description: The list of definition of individual key blobs including plain data. Add other array items as you need and device allows type: array minItems: 1 @@ -168,3 +177,59 @@ iee: description: End address of key blob data, it should be aligned to 1 KB (1024 B) format: number template_value: "0x30008000" + +master_id: + type: object + title: Master ID for MIMXRT118x core + properties: + master: + type: string + title: Master ID for RT118x + description: M33 or M7 core + enum: ["CM33", "CM7"] + template_value: "CM7" + +key_blobs_rt118x: + type: object + title: MIMXRT118x Keyblob Configuration + properties: + key_blob: + description: Keyblob configuration, keyblob won't be generated. + type: object + required: [key1, key2, aes_mode, key_size] + + properties: + aes_mode: + type: string + title: AES mode + description: AES mode, Encryption bypass, AES-XTS, AES-CTR (with or without address binding) or AES-CTR keystream only + enum: + [ + "Bypass", + "AesXTS", + "AesCTRWAddress", + "AesCTRWOAddress", + "AesCTRkeystream", + ] + template_value: "AesXTS" + + key_size: + type: string + title: AES key size, 128/256 for AES-CTR or 256/512 for AES-XTS + description: AES mode, AES-XTS or AES-CTR + enum: ["CTR256XTS512", "CTR128XTS256"] + template_value: "CTR256XTS512" + + key1: + type: [number, string] + title: AES-XTS key1 / AES-CTR key + description: AES key for the key blob, size depends on key_size + format: number + template_value: "0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" + + key2: + type: [number, string] + title: AES-CTR Counter value or AES-XTS key2 + description: AES key for the key blob, size depends on key_size + format: number + template_value: "0x202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F" diff --git a/spsdk/data/utils/otfad/database.yml b/spsdk/data/utils/otfad/database.yaml similarity index 50% rename from spsdk/data/utils/otfad/database.yml rename to spsdk/data/utils/otfad/database.yaml index e362c4eb..a6a44f6c 100644 --- a/spsdk/data/utils/otfad/database.yml +++ b/spsdk/data/utils/otfad/database.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -12,13 +12,11 @@ devices: width: 256 reverse_subregs_order: True config_as_hexstring: true - description: - OTP MASTER KEY. + description: OTP MASTER KEY. - name: OTFAD_KEK_SEED width: 128 config_as_hexstring: true - description: - OTFAD KEK SEED + description: OTFAD KEK SEED rt6xx: latest: b0 @@ -30,17 +28,17 @@ devices: width: 256 reverse_subregs_order: True config_as_hexstring: true - description: - OTP MASTER KEY. + description: OTP MASTER KEY. - name: OTFAD_KEK_SEED width: 128 config_as_hexstring: true - description: - OTFAD KEK SEED + description: OTFAD KEK SEED - rt1180: + rt118x: latest: a0 attributes: + otfad_key_fuse: "OTFAD{index}_KEY" + otfad_cfg_fuse: "OTFAD{index}_CFG4" reg_fuses: fuses_rt118x.xml keyblob_byte_swap_cnt: 8 sb_21_supported: False @@ -60,9 +58,37 @@ devices: width: 128 reverse_subregs_order: True config_as_hexstring: true - description: - OTFAD Key knows as KEK. + description: OTFAD Key known as KEK. + rt117x: + latest: a0 + attributes: + reg_fuses: fuses_rt117x.xml + keyblob_byte_swap_cnt: 0 + sb_21_supported: False + has_kek_fuses: True + peripheral_list: ["1", "2"] + supports_key_scrambling: True + additional_template: ["otfad_scramble"] + additional_template_text: | + Is important to use physical addresses (non-secure) in settings of OTFAD to proper run the decryption on device. + + The fuse word 0x47 on address 0xC70 uses error-correcting codes. + The fuse word can only be written one time, so you must set all of the desired bits within the word at the same time. + Refer to the SRM to get familiar with the functionality of all bitfields. + The ENCRYPT_XIP_EN fuse is part of the BOOT_CFG1 fuse. If this bit is set, all BOOT_CFG fuses must be configured accordingly. + The encrypted XiP can be also enabled by the BOOT_CFG pin + + It is recommended to set the USER_KEY5_RLOCK and OTFAD1/2_KEY_SEL_LOCK fuses for production to protect sensitive data stored in the USER_KEY5 fuse (if used) and to prevent a modification of the OTFAD key selection by malicious software. + grouped_registers: + - name: OTFAD_KEY + width: 128 + reverse_subregs_order: True + config_as_hexstring: true + description: OTFAD Key known as KEK. + + rt116x: + device_alias: rt117x # default values for all devices attributes: @@ -77,4 +103,9 @@ attributes: additional_template: [] additional_template_text: "" supports_key_scrambling: False - + otfad_key_fuse: "OTFAD_KEY" + otfad_cfg_fuse: "OTFAD_CFG" + otfad_enable_bitfield: "OTFAD_ENABLE" + otfad_scramble_enable_bitfield: "OTFAD_SCRAMBLE_ENABLE" + otfad_scramble_align_bitfield: "OTFAD_SCRAMBLE_ALIGN" + otfad_scramble_key: "OTFAD{index}_KEY_SCRAMBLE" diff --git a/spsdk/data/utils/otfad/fuses_rt117x.xml b/spsdk/data/utils/otfad/fuses_rt117x.xml new file mode 100644 index 00000000..ad7e015f --- /dev/null +++ b/spsdk/data/utils/otfad/fuses_rt117x.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spsdk/data/utils/otfad/fuses_rt118x.xml b/spsdk/data/utils/otfad/fuses_rt118x.xml index c521cdb4..8e1fa98f 100644 --- a/spsdk/data/utils/otfad/fuses_rt118x.xml +++ b/spsdk/data/utils/otfad/fuses_rt118x.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/utils/otfad/fuses_rt5xx.xml b/spsdk/data/utils/otfad/fuses_rt5xx.xml index 30bc2e44..196fbf05 100644 --- a/spsdk/data/utils/otfad/fuses_rt5xx.xml +++ b/spsdk/data/utils/otfad/fuses_rt5xx.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/utils/otfad/fuses_rt6xx.xml b/spsdk/data/utils/otfad/fuses_rt6xx.xml index 30bc2e44..196fbf05 100644 --- a/spsdk/data/utils/otfad/fuses_rt6xx.xml +++ b/spsdk/data/utils/otfad/fuses_rt6xx.xml @@ -1,4 +1,9 @@ + diff --git a/spsdk/data/utils/otfad/sch_otfad.yml b/spsdk/data/utils/otfad/sch_otfad.yaml similarity index 89% rename from spsdk/data/utils/otfad/sch_otfad.yml rename to spsdk/data/utils/otfad/sch_otfad.yaml index 1ba0014d..07d2a8a8 100644 --- a/spsdk/data/utils/otfad/sch_otfad.yml +++ b/spsdk/data/utils/otfad/sch_otfad.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -27,23 +27,23 @@ otfad_output: output_name: type: string title: Output binary image file name - description: File name of the output image containing keyblobs and encrypted data blobs - template_value: "encrypted.bin" - default: "encrypted.bin" + description: Filename of the output image containing keyblobs and encrypted data blob without file extension (.bin will be appended) + template_value: "otfad_whole_image" + default: "otfad_whole_image" keyblob_name: type: string title: Keyblob file name - description: File name of the keyblob, output_folder/keyblob_name - template_value: "iee_keyblob.bin" - default: "iee_keyblob.bin" + description: Filename of the keyblob without file extension (.bin will be appended) + template_value: "OTFAD_Table" + default: "OTFAD_Table" encrypted_name: type: string title: Encrypted name - description: filename of the encrypted datablobs - template_value: encrypted_blob.bin - default: encrypted_blob.bin + description: filename of the encrypted datablobs without file extension (.bin will be appended) + template_value: encrypted_blob + default: encrypted_blob required: [output_folder] otfad: @@ -55,7 +55,6 @@ otfad: type: string title: KEK description: OTFAD Key Encryption Key to encrypt OTFAD table - format: file template_value: my_secret_kek.bin otfad_table_address: @@ -166,4 +165,3 @@ otfad_scramble: description: OTFAD Key scramble mask align (1 byte size) format: number template_value: "0x72" - diff --git a/spsdk/data/utils/sch_crypto.yml b/spsdk/data/utils/sch_crypto.yaml similarity index 67% rename from spsdk/data/utils/sch_crypto.yml rename to spsdk/data/utils/sch_crypto.yaml index f3990846..59a1ca54 100644 --- a/spsdk/data/utils/sch_crypto.yml +++ b/spsdk/data/utils/sch_crypto.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -113,80 +113,96 @@ certificate_v2: format: optional_file template_value: chain_certificate3_depth3.pem -certificate_v2_chain_id: - type: object - title: Certificate V2 Settings - properties: - mainRootCertId: - type: [number, string] - title: Main Certificate Index - description: Index of certificate that is used as a main. - format: number - template_value: 0 - - mainCertChainId: - type: [number, string] - title: Main Certificate Chain Index - description: Caution! This property is kept here for backwards compatibility with old schemas. Use mainRootCertId instead. - format: number - skip_in_template: true - - anyOf: - - required: - - mainRootCertId - - required: - - mainCertChainId certificate_v31: type: object - title: Certificate V3.1 Settings + title: ISK Certificate Settings properties: + binaryCertificateBlock: + type: string + title: Binary Certificate + description: Optionally the certificate could be defined as a pre-generated binary block. + In case that is defined, all other configuration for certification block is omitted expect the 'signingCertificatePrivateKeyFile' or 'iskSignProvider' + format: file + template_value: my_isk_cert.bin useIsk: type: boolean title: Use ISK for signature certification - description: Enable ISK type of signature certification + description: Enable ISK type of signature certification. Unused when 'binaryCertificateBlock' is defined template_value: false mainRootCertPrivateKeyFile: type: string title: Main root Certification Private Key - description: Path to Main root Certification Private Key + description: Path to Main root Certification Private Key. Unused when 'binaryCertificateBlock' is defined format: file template_value: main_cert_prv_key.pem signingCertificateFile: type: string title: Signing Certificate - description: Path to Signing Certificate + description: Path to Signing Certificate. Unused when 'binaryCertificateBlock' is defined format: file template_value: sign_cert.pem signingCertificateConstraint: type: [string, number] title: Signing certificate constrain - description: Signing certificate constrain number + description: Signing certificate constrain number. Unused when 'binaryCertificateBlock' is defined format: number + default: 0 template_value: 0 signCertData: type: string title: Signing Certificate data - description: Path to Signing Certificate data + description: Path to Signing Certificate data. Unused when 'binaryCertificateBlock' is defined format: optional_file template_value: sign_cert.bin + signingCertificatePrivateKeyFile: + id: "#signingCertificatePrivateKeyFile" + type: string + title: ISK Certificate private key + description: ISK Certificate private key used to sign certificate. It can be replaced by signProvider key. + format: file + template_value: isk_prv_key.pem + iskSignProvider: + type: string + title: ISK Signature Provider + description: Signature provider configuration in format 'type=;=;=". + template_value: type=file;file_path=my_isk_prv_key.pem - anyOf: - - required: - - mainRootCertId - - required: - - mainCertChainId - if: - properties: - useIsk: - const: True - then: - required: [mainRootCertPrivateKeyFile, signingCertificateFile] + allOf: # Global all of group - this is main concatenation group for all sub rules + - oneOf: + - required: [binaryCertificateBlock] + - required: [useIsk] + if: + properties: + useIsk: + const: True + then: + required: [signingCertificateFile] + oneOf: + - allOf: + - required: [mainRootCertPrivateKeyFile] + - not: + required: [signProvider] + - allOf: + - required: [signProvider] + - not: + required: [mainRootCertPrivateKeyFile] +cert_block_output: + type: object + title: Basic Settings + properties: + containerOutputFile: + type: string + title: cert block filename + description: Generated cert block filename. + template_value: cert_block.bin + required: [cert_block_output] + certificate_root_keys: type: object - title: Certificate Settings + title: Root Keys Settings properties: rootCertificate0File: type: string @@ -216,7 +232,7 @@ certificate_root_keys: mainRootCertId: type: [number, string] title: Main Certificate Index - description: Index of certificate that is used as a main. + description: Index of certificate that is used as a main. If not defined, the certificate matching private key will be selected. format: number template_value: 0 @@ -227,4 +243,15 @@ certificate_root_keys: format: number skip_in_template: true - required: [rootCertificate0File] + allOf: # Global all of group - this is main concatenation group for all sub rules + - oneOf: + - required: [binaryCertificateBlock] + - allOf: + - required: [rootCertificate0File] + - anyOf: + - required: [mainCertPrivateKeyFile] + - anyOf: + - required: [mainRootCertId] + - required: [mainCertChainId] + + diff --git a/spsdk/debuggers/debug_probe.py b/spsdk/debuggers/debug_probe.py index d40475e2..cade20d2 100644 --- a/spsdk/debuggers/debug_probe.py +++ b/spsdk/debuggers/debug_probe.py @@ -12,6 +12,7 @@ from typing import Any, Dict, List, Optional, no_type_check from spsdk import SPSDKError +from spsdk.exceptions import SPSDKValueError from spsdk.utils.exceptions import SPSDKTimeoutError from spsdk.utils.misc import Timeout @@ -237,10 +238,10 @@ def get_coresight_ap_address(cls, access_port: int, address: int) -> int: :param access_port: Index of access port 0-255. :param address: Register address. :return: Coresight address. - :raises ValueError: In case of invalid value. + :raises SPSDKError: In case of invalid value. """ if access_port > 255: - raise ValueError() + raise SPSDKValueError("Invalid value of access port") return access_port << cls.APSEL_SHIFT | address @@ -328,6 +329,7 @@ def _target_power_control(self, sys_power: bool = False, debug_power: bool = Fal :param sys_power: Control the target system power state. :param debug_power: Control the target debug power state. + :raises SPSDKTimeoutError: Timeout on power enable operation. """ logger.debug( f"Power Control the debug connection:\nSystem power: {sys_power}\nDebug power: {debug_power}" diff --git a/spsdk/debuggers/debug_probe_pemicro.py b/spsdk/debuggers/debug_probe_pemicro.py index 58355d92..5294ea98 100644 --- a/spsdk/debuggers/debug_probe_pemicro.py +++ b/spsdk/debuggers/debug_probe_pemicro.py @@ -82,15 +82,18 @@ def get_connected_probes( # pylint: disable=import-outside-toplevel from .utils import DebugProbes, ProbeDescription - pemicro = DebugProbePemicro.get_pemicro_lib() - probes = DebugProbes() - connected_probes = pemicro.list_ports() - for probe in connected_probes: - probes.append( - ProbeDescription("PEMicro", probe["id"], probe["description"], DebugProbePemicro) - ) - + try: + connected_probes = PyPemicro.list_ports() + for probe in connected_probes: + if not hardware_id or hardware_id == str(probe["id"]): + probes.append( + ProbeDescription( + "PEMicro", probe["id"], probe["description"], DebugProbePemicro + ) + ) + except PEMicroException as exc: + logger.warning(f"Cannot get list of PEMicro probes: {str(exc)}") return probes def open(self) -> None: diff --git a/spsdk/debuggers/debug_probe_pyocd.py b/spsdk/debuggers/debug_probe_pyocd.py index fe534621..7479bf0e 100644 --- a/spsdk/debuggers/debug_probe_pyocd.py +++ b/spsdk/debuggers/debug_probe_pyocd.py @@ -112,15 +112,10 @@ def open(self) -> None: self.probe.session = Session(self.probe) self.probe.open() - if isinstance(self.probe, JLinkProbe): - self.probe._link.set_tif(pylink.enums.JLinkInterfaces.SWD) - self.probe._link.coresight_configure() - self._protocol = pylink.enums.JLinkInterfaces.SWD - else: - self.probe.connect(pyocd.probe.debug_probe.DebugProbe.Protocol.SWD) + self.probe.connect(pyocd.probe.debug_probe.DebugProbe.Protocol.SWD) # Do reset sequence to switch to used protocol connector = DPConnector(self.probe) - connector._connect_dp(pyocd.probe.debug_probe.DebugProbe.Protocol.SWD) + connector.connect() logger.debug(connector._idr) # Power Up the system and debug and clear sticky errors self.clear_sticky_errors() diff --git a/spsdk/debuggers/utils.py b/spsdk/debuggers/utils.py index 9967f273..c6ba667d 100644 --- a/spsdk/debuggers/utils.py +++ b/spsdk/debuggers/utils.py @@ -225,6 +225,6 @@ def open_debug_probe( try: yield debug_probe except SPSDKError as exc: - raise exc + raise SPSDKError("Problem with debug probe occurred") from exc finally: debug_probe.close() diff --git a/spsdk/exceptions.py b/spsdk/exceptions.py index 982d264b..96697885 100644 --- a/spsdk/exceptions.py +++ b/spsdk/exceptions.py @@ -64,3 +64,15 @@ class SPSDKAlignmentError(SPSDKError, ValueError): class SPSDKParsingError(SPSDKError): """Cannot parse binary data.""" + + +class SPSDKCorruptedException(SPSDKError): + """Corrupted Exception.""" + + +class SPSDKUnsupportedOperation(SPSDKError): + """SPSDK unsupported operation error.""" + + +class SPSDKSyntaxError(SyntaxError, SPSDKError): + """SPSDK syntax error.""" diff --git a/spsdk/image/__init__.py b/spsdk/image/__init__.py index 9936390e..4e0054f2 100644 --- a/spsdk/image/__init__.py +++ b/spsdk/image/__init__.py @@ -2,7 +2,7 @@ # -*- coding: UTF-8 -*- # # Copyright 2017-2018 Martin Olejar -# Copyright 2019-2022 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -13,9 +13,8 @@ from spsdk import SPSDK_DATA_FOLDER IMG_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "image") -TZ_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_tz.yml") -MBIMG_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_mbimg.yml") -SB3_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_sb3.yml") +TZ_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_tz.yaml") +MBIMG_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_mbimg.yaml") from .bee import BeeFacRegion, BeeKIB, BeeProtectRegionBlock, BeeRegionHeader from .commands import ( diff --git a/spsdk/image/ahab/__init__.py b/spsdk/image/ahab/__init__.py index 5fb80db5..5b25cda3 100644 --- a/spsdk/image/ahab/__init__.py +++ b/spsdk/image/ahab/__init__.py @@ -11,6 +11,6 @@ from spsdk import SPSDK_DATA_FOLDER AHAB_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "ahab") -AHAB_SCH_FILE: str = os.path.join(AHAB_DATA_FOLDER, "sch_ahab.yml") -AHAB_DATABASE_FILE: str = os.path.join(AHAB_DATA_FOLDER, "database.yml") -SIGNED_MSG_SCH_FILE: str = os.path.join(AHAB_DATA_FOLDER, "sch_signed_msg.yml") +AHAB_SCH_FILE: str = os.path.join(AHAB_DATA_FOLDER, "sch_ahab.yaml") +AHAB_DATABASE_FILE: str = os.path.join(AHAB_DATA_FOLDER, "database.yaml") +SIGNED_MSG_SCH_FILE: str = os.path.join(AHAB_DATA_FOLDER, "sch_signed_msg.yaml") diff --git a/spsdk/image/ahab/ahab_container.py b/spsdk/image/ahab/ahab_container.py index 592bff7c..422349b4 100644 --- a/spsdk/image/ahab/ahab_container.py +++ b/spsdk/image/ahab/ahab_container.py @@ -254,8 +254,7 @@ def image(self) -> bytes: if self.flags_is_encrypted: return self.encrypted_image - else: - return self.plain_image + return self.plain_image # We need to extend the format, as the base provides only endianness. @classmethod @@ -316,7 +315,7 @@ def create_flags( :return: Image flags data field. """ flags_data = ImageArrayEntry.FLAGS_TYPES[image_type] - flags_data |= {"cortex-m33": 0x1, "cortex-m7": 0x02}[core_id] << 4 + flags_data |= {"cortex-m33": 0x1, "cortex-m7": 0x02, "cortex-a55": 0x02}[core_id] << 4 flags_data |= {"sha256": 0x0, "sha384": 0x1, "sha512": 0x2}[ hash_type ] << ImageArrayEntry.FLAGS_HASH_OFFSET @@ -470,8 +469,8 @@ def parse(parent: "AHABContainer", binary: bytes, offset: int = 0) -> "ImageArra :param parent: Parent AHABContainer object. :param binary: Binary data with Image Array Entry block to parse. :param offset: Offset to Image Array Entry block data, default is 0. - :raise SPSDKLengthError: If invalid length of image is detected. - :raise SPSDKValueError: Invalid hash for image. + :raises SPSDKLengthError: If invalid length of image is detected. + :raises SPSDKValueError: Invalid hash for image. :return: Object recreated from the binary data. """ binary_size = len(binary) @@ -1809,7 +1808,7 @@ def load_from_config( :param config: Blob configuration :param search_paths: List of paths where to search for the file, defaults to None - :raises SPSDKError: Invalid configuration - Invalid DEK Keyblob + :raises SPSDKValueError: Invalid configuration - Invalid DEK KeyBlob :return: Blob object. """ dek_size = value_to_int(config.get("dek_key_size", 128)) @@ -1823,7 +1822,7 @@ def load_from_config( dek_keyblob_input, Blob.compute_keyblob_size(dek_size) + 8, search_paths ) if not dek_keyblob_value: - raise SPSDKValueError("Invalid DEK Keyblob.") + raise SPSDKValueError("Invalid DEK KeyBlob.") keyblob = Blob.parse(dek_keyblob_value) keyblob.dek = dek @@ -2680,7 +2679,6 @@ def decrypt_data(self) -> None: """Decrypt all images if possible.""" for ix, image_entry in enumerate(self.image_array): if image_entry.flags_is_encrypted and self.signature_block.blob: - decrypted_data = self.signature_block.blob.decrypt_data( image_entry.image_iv[16:], image_entry.encrypted_image ) @@ -2922,7 +2920,7 @@ def add_container(self, container: AHABContainer) -> None: The order of the added images is important. :param container: New AHAB Container to be added. - :raise SPSDKLengthError: The container count in image is overflowed. + :raises SPSDKLengthError: The container count in image is overflowed. """ if len(self.ahab_containers) >= self.containers_max_cnt: raise SPSDKLengthError( @@ -3075,7 +3073,7 @@ def validate(self) -> None: self.image_info().validate() except SPSDKError as exc: logger.error(self.image_info().draw()) - raise exc + raise SPSDKError("Validation failed") from exc @staticmethod def load_from_config( @@ -3092,7 +3090,7 @@ def load_from_config( """ containers_config: List[Dict[str, Any]] = config["containers"] family = config["family"] - revision = config["revision"] + revision = config.get("revision", "latest") image_type = config["image_type"] ahab = AHABImage( family=family, revision=revision, image_type=image_type, search_paths=search_paths diff --git a/spsdk/image/ahab/signed_msg.py b/spsdk/image/ahab/signed_msg.py index d4253d17..30969567 100644 --- a/spsdk/image/ahab/signed_msg.py +++ b/spsdk/image/ahab/signed_msg.py @@ -686,7 +686,7 @@ def validate(self, data: Dict[str, Any]) -> None: f"{len(self.encrypt_iv)*8} Bits != {self.ENCRYPT_IV_LEN * 8} Bits" ) if self.message is None: - raise SPSDKValueError(f"Signed Message: Invalid Message payload.") + raise SPSDKValueError("Signed Message: Invalid Message payload.") self.message.validate() @staticmethod diff --git a/spsdk/image/bee.py b/spsdk/image/bee.py index 566b7da4..1208b13b 100644 --- a/spsdk/image/bee.py +++ b/spsdk/image/bee.py @@ -35,7 +35,7 @@ BEE_DATA_FOLDER: str = os.path.join(UTILS_DATA_FOLDER, "bee") -BEE_SCH_FILE: str = os.path.join(BEE_DATA_FOLDER, "sch_bee.yml") +BEE_SCH_FILE: str = os.path.join(BEE_DATA_FOLDER, "sch_bee.yaml") BEE_DATABASE_FILE: str = os.path.join(BEE_DATA_FOLDER, "database.yaml") # maximal size of encrypted block in bytes @@ -535,15 +535,15 @@ def export(self, dbg_info: DebugInfo = DebugInfo.disabled()) -> bytes: # KIB kib_data = self._kib.export() dbg_info.append_binary_section("BEE-KIB (non-crypted)", kib_data) - aes = AES.new(self._sw_key, AES.MODE_ECB) - result += aes.encrypt(kib_data) + aes_ecb = AES.new(self._sw_key, AES.MODE_ECB) + result += aes_ecb.encrypt(kib_data) # padding result = extend_block(result, self.PRDB_OFFSET) # PRDB prdb_data = self._prdb.export() dbg_info.append_binary_section("BEE-PRDB (non-crypted)", prdb_data) - aes = AES.new(self._kib.kib_key, AES.MODE_CBC, self._kib.kib_iv) - result += aes.encrypt(prdb_data) + aes_cbc = AES.new(self._kib.kib_key, AES.MODE_CBC, self._kib.kib_iv) + result += aes_cbc.encrypt(prdb_data) # padding return extend_block(result, self.SIZE) @@ -560,11 +560,11 @@ def parse(cls, data: bytes, offset: int = 0, sw_key: bytes = b"") -> "BeeRegionH super().parse(data, offset) # check size of the input data if len(sw_key) != 16: raise SPSDKError("Invalid sw key") - aes = AES.new(sw_key, AES.MODE_ECB) - decr_data = aes.decrypt(data[offset : offset + BeeKIB._size()]) + aes_ecb = AES.new(sw_key, AES.MODE_ECB) + decr_data = aes_ecb.decrypt(data[offset : offset + BeeKIB._size()]) kib = BeeKIB.parse(decr_data) - aes = AES.new(kib.kib_key, AES.MODE_CBC, kib.kib_iv) - decr_data = aes.decrypt( + aes_cbc = AES.new(kib.kib_key, AES.MODE_CBC, kib.kib_iv) + decr_data = aes_cbc.decrypt( data[offset + cls.PRDB_OFFSET : offset + cls.PRDB_OFFSET + BeeProtectRegionBlock.SIZE] ) prdb = BeeProtectRegionBlock.parse(decr_data) diff --git a/spsdk/image/bootable_image/__init__.py b/spsdk/image/bootable_image/__init__.py index bdf913f2..3f5bee0b 100644 --- a/spsdk/image/bootable_image/__init__.py +++ b/spsdk/image/bootable_image/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -11,5 +11,5 @@ from spsdk import SPSDK_DATA_FOLDER BIMG_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "image", "bootable_image") -BIMG_SCH_FILE: str = os.path.join(BIMG_DATA_FOLDER, "sch_bimg.yml") -BIMG_DATABASE_FILE: str = os.path.join(BIMG_DATA_FOLDER, "database.yml") +BIMG_SCH_FILE: str = os.path.join(BIMG_DATA_FOLDER, "sch_bimg.yaml") +BIMG_DATABASE_FILE: str = os.path.join(BIMG_DATA_FOLDER, "database.yaml") diff --git a/spsdk/image/bootable_image/bimg.py b/spsdk/image/bootable_image/bimg.py index 60f484a4..e1bef504 100644 --- a/spsdk/image/bootable_image/bimg.py +++ b/spsdk/image/bootable_image/bimg.py @@ -8,36 +8,22 @@ """This module contains Bootable image related code.""" import abc -import inspect import logging import os import re import sys from copy import deepcopy -from typing import Any, Dict, List, Optional, Type, Union +from enum import Enum +from typing import Any, Dict, List, Optional, Type -from spsdk.exceptions import SPSDKKeyError, SPSDKValueError +from spsdk.exceptions import SPSDKKeyError, SPSDKParsingError, SPSDKTypeError, SPSDKValueError from spsdk.image.bootable_image import BIMG_DATABASE_FILE, BIMG_SCH_FILE from spsdk.image.fcb.fcb import FCB -from spsdk.image.header import UnparsedException -from spsdk.image.images import BootImgRT -from spsdk.image.segments import ( - AbstractFCB, - FlexSPIConfBlockFCB, - PaddingFCB, - SegAPP, - SegBDT, - SegBEE, - SegCSF, - SegDCD, - SegIVT2, - SegXMCD, - XMCDHeader, -) +from spsdk.image.segments import FlexSPIConfBlockFCB, XMCDHeader from spsdk.image.xmcd.xmcd import XMCD, ConfigurationBlockType, MemoryType from spsdk.utils.database import Database from spsdk.utils.images import BinaryImage, BinaryPattern -from spsdk.utils.misc import DebugInfo, find_first, load_binary, write_file +from spsdk.utils.misc import load_binary, write_file from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas, check_config logger = logging.getLogger(__name__) @@ -45,11 +31,53 @@ BIMG_CLASSES = [ "BootableImageRtxxx", "BootableImageLpc55s3x", - "BootableImageRt1xxx", + "BootableImageRt101x", + "BootableImageRt10xx", + "BootableImageRt11xx", "BootableImageRt118x", ] +class EnumBimgSegments(str, Enum): + """Bootable image segments.""" + + KEYBLOB = "keyblob" + FCB = "fcb" + IMAGE_VERSION = "image_version" + KEYSTORE = "keystore" + APPLICATION = "application" + BEE_HEADER_0 = "bee_header_0" + BEE_HEADER_1 = "bee_header_1" + XMCD = "xmcd" + HAB_CONTAINER = "hab_container" + AHAB_CONTAINER = "ahab_container" + + @staticmethod + def get_segment_type(segment: "EnumBimgSegments") -> Type: + """Get the output value type. + + :param segment: Bootable image segment + :return: True is the segment output type is binary. False otherwise. + """ + if segment == EnumBimgSegments.IMAGE_VERSION: + return int + return bytes + + +def convert_segment_value(value: bytes, output_type: Type) -> Any: + """Convert value of segment into desired type. + + :param value: Segment value as bytes. + :param output_type: Output type. + :return: Converted value. + """ + if output_type == int: + return int.from_bytes(value, "little") + if output_type == bytes: + return value + raise SPSDKTypeError(f"Unsupported output type {output_type}") + + def get_bimg_class(family: str) -> Type["BootableImage"]: """Get the class that supports the family. @@ -67,6 +95,8 @@ def get_bimg_class(family: str) -> Type["BootableImage"]: class BootableImage: """Bootable Image class.""" + IMAGE_PATTERN = BinaryPattern("zeros") + def __init__(self, family: str, mem_type: str, revision: str = "latest") -> None: """Bootable Image constructor. @@ -86,6 +116,110 @@ def __init__(self, family: str, mem_type: str, revision: str = "latest") -> None raise SPSDKValueError(f"Unsupported memory type: {mem_type}") self.bimg_descr: Dict = self.mem_types[self.mem_type] + @staticmethod + @abc.abstractmethod + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. + + :param mem_type: Used memory type. + """ + + def get_segment_size(self, segment: EnumBimgSegments) -> int: + """Get size of segment. Return -1 if it is the last segment.""" + segments = { + EnumBimgSegments.FCB: 512, + EnumBimgSegments.BEE_HEADER_0: 512, + EnumBimgSegments.BEE_HEADER_1: 512, + EnumBimgSegments.KEYBLOB: 256, + EnumBimgSegments.KEYSTORE: 2048, + EnumBimgSegments.IMAGE_VERSION: 4, + EnumBimgSegments.APPLICATION: -1, + EnumBimgSegments.HAB_CONTAINER: -1, + EnumBimgSegments.AHAB_CONTAINER: -1, + } + return segments[segment] + + def parse(self, binary: bytes) -> None: + """Parse binary into bootable image object. + + :param binary: Full binary of bootable image. + """ + for segment in self.get_segments(self.mem_type): + offset_from = self.bimg_descr[f"{segment.value}_offset"] + offset_to = ( + offset_from + self.get_segment_size(segment) + if self.get_segment_size(segment) != -1 + else None + ) + value = binary[offset_from:offset_to] + setattr(self, segment.value, value) + + @staticmethod + def get_validation_schemas( + family: str, mem_type: str, revision: str = "latest" + ) -> List[Dict[str, Any]]: + """Get validation schema for the family. + + :param family: Chip family + :param mem_type: Used memory type. + :param revision: Chip revision specification, as default, latest is used. + :return: List of validation schema dictionaries. + """ + bimg_cls = get_bimg_class(family) + sch_cfg = deepcopy(ValidationSchemas.get_schema_file(BIMG_SCH_FILE)) + sch_cfg["family_rev"]["properties"]["family"]["enum"] = bimg_cls.get_supported_families() + sch_cfg["family_rev"]["properties"]["revision"]["enum"] = bimg_cls.get_supported_revisions( + family + ) + + sch_cfg["family_rev"]["properties"]["memory_type"][ + "enum" + ] = bimg_cls.get_supported_memory_types(family, revision) + schemas = [sch_cfg["family_rev"]] + for segment in bimg_cls.get_segments(mem_type): + schemas.append(sch_cfg[segment.value]) + return schemas + + def store_config(self, output: str) -> None: + """Store bootable image into configuration and binary blocks. + + :param output: Path to output folder to store bootable image configuration. + """ + schemas = self._get_validation_schemas() + override: Dict[str, str] = {} + override["family"] = self.family + override["revision"] = self.revision + override["memory_type"] = self.mem_type + for segment in self.get_segments(self.mem_type): + segment_value = getattr(self, segment.value) + if segment_value is not None: + segment_value = convert_segment_value( + segment_value, EnumBimgSegments.get_segment_type(segment) + ) + if isinstance(segment_value, bytes): + override[segment.value] = f"{segment.value}.bin" + else: + override[segment.value] = segment_value + else: + override[segment.value] = "" + + config = ConfigTemplate( + f"Bootable Image Configuration for {self.family}.", + schemas, + override, + ).export_to_yaml() + write_file( + config, + os.path.join(output, f"bootable_image_{self.family}_{self.mem_type}.yaml"), + ) + for segment in self.get_segments(self.mem_type): + segment_value = getattr(self, segment.value) + if segment_value is not None: + if EnumBimgSegments.get_segment_type(segment) == bytes: + write_file( + segment_value, os.path.join(output, f"{segment.value}.bin"), mode="wb" + ) + @classmethod def load_from_config( cls, config: Dict, search_paths: Optional[List[str]] = None @@ -95,16 +229,46 @@ def load_from_config( :param config: Configuration of Bootable image. :param search_paths: List of paths where to search for the file, defaults to None """ - check_config(config, cls.get_validation_schemas_family()) bimg_cls = get_bimg_class(config["family"]) - return bimg_cls.load_from_config(config, search_paths=search_paths) + check_config(config, cls.get_validation_schemas_family()) + chip_family = config["family"] + mem_type = config["memory_type"] + revision = config.get("revision", "latest") + schemas = cls.get_validation_schemas(chip_family, mem_type, revision) + check_config(config, schemas, search_paths=search_paths) + params = {"family": chip_family, "mem_type": mem_type, "revision": revision} + for segment in bimg_cls.get_segments(mem_type): + segment_value = config.get(segment.value) + if EnumBimgSegments.get_segment_type(segment) == bytes: + value = ( + load_binary(segment_value, search_paths=search_paths) if segment_value else None + ) + else: + value = segment_value + params[segment.value] = value + return bimg_cls(**params) - @abc.abstractmethod - def store_config(self, output: str) -> None: - """Store bootable image into configuration and binary blocks. + def image_info(self) -> BinaryImage: + """Create Binary image of bootable image. - :param output: Path to output folder to store bootable image configuration. + :return: BinaryImage object of bootable image. """ + bin_image = BinaryImage( + name=f"Bootable Image for {self.family}", size=0, pattern=self.IMAGE_PATTERN + ) + for segment in self.get_segments(self.mem_type): + segment_value = getattr(self, segment.value) + if segment_value: + bin_image.add_image( + BinaryImage( + name=segment.value, + size=len(segment_value), + offset=self.bimg_descr[f"{segment.value}_offset"], + binary=segment_value, + parent=bin_image, + ) + ) + return bin_image def export(self) -> bytes: """Export bootable image. @@ -113,20 +277,6 @@ def export(self) -> bytes: """ return self.image_info().export() - @abc.abstractmethod - def parse(self, binary: bytes) -> None: - """Parse binary into bootable image object. - - :param binary: Complete binary of bootable image. - """ - - @abc.abstractmethod - def image_info(self) -> BinaryImage: - """Create Binary image of bootable image. - - :return: BinaryImage object of bootable image. - """ - @staticmethod def get_validation_schemas_family() -> List[Dict[str, Any]]: """Create the validation schema just for supported families. @@ -141,16 +291,7 @@ def _get_validation_schemas(self) -> List[Dict[str, Any]]: :return: List of validation schema dictionaries. """ - return self.get_validation_schemas(self.family) - - @staticmethod - def get_validation_schemas(family: str) -> List[Dict[str, Any]]: - """Get validation schema for the family. - - :param family: Chip family - :return: List of validation schema dictionaries. - """ - return get_bimg_class(family).get_validation_schemas(family) + return self.get_validation_schemas(self.family, self.mem_type, self.revision) @staticmethod def generate_config_template(family: str, mem_type: str, revision: str = "latest") -> str: @@ -161,7 +302,7 @@ def generate_config_template(family: str, mem_type: str, revision: str = "latest :param revision: Chip revision specification, as default, latest is used. :return: Validation schema. """ - schemas = BootableImage.get_validation_schemas(family) + schemas = BootableImage.get_validation_schemas(family, mem_type, revision) override = {} override["family"] = family override["revision"] = revision @@ -190,6 +331,21 @@ def get_supported_memory_types(family: str, revision: str = "latest") -> List[st database = Database(BIMG_DATABASE_FILE) return list(database.get_device_value("mem_types", family, revision).keys()) + @staticmethod + def get_memory_type_config( + family: str, mem_type: str, revision: str = "latest" + ) -> Dict[str, Any]: + """Return dictionary with configuration for specific memory type. + + :raises SPSDKKeyError: If memory type does not exist in database + :return: Dictionary with configuration. + """ + if mem_type not in BootableImage.get_supported_memory_types(family): + raise SPSDKKeyError(f"Memory type not supported: {mem_type}") + database = Database(BIMG_DATABASE_FILE) + mem_types: Dict = database.get_device_value("mem_types", family, revision) + return mem_types[mem_type] + @staticmethod def get_supported_revisions(family: str) -> List[str]: """Return list of supported revisions. @@ -222,9 +378,9 @@ def __init__( revision: str = "latest", keyblob: Optional[bytes] = None, fcb: Optional[bytes] = None, - image_version: int = 0, + image_version: Optional[int] = None, keystore: Optional[bytes] = None, - app: Optional[bytes] = None, + application: Optional[bytes] = None, ) -> None: """Bootable Image constructor for RTxxx devices. @@ -236,137 +392,28 @@ def __init__( """ super().__init__(family, mem_type, revision) self.keyblob = keyblob - self.fcb = None - if fcb: - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(fcb) + self.fcb = fcb + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) - self.image_version = image_version + self.image_version = (image_version or 0).to_bytes(4, "little") self.keystore = keystore - self.app = app + self.application = application @staticmethod - def get_validation_schemas(family: str, revision: str = "latest") -> List[Dict[str, Any]]: - """Get validation schema for the family. - - :param family: Chip family - :param revision: Chip revision specification, as default, latest is used. - :return: List of validation schema dictionaries. - """ - sch_cfg = deepcopy(ValidationSchemas.get_schema_file(BIMG_SCH_FILE)) - sch_cfg["family_rev"]["properties"]["family"][ - "enum" - ] = BootableImageRtxxx.get_supported_families() - sch_cfg["family_rev"]["properties"]["revision"][ - "enum" - ] = BootableImageRtxxx.get_supported_revisions(family) - sch_cfg["family_rev"]["properties"]["memory_type"][ - "enum" - ] = BootableImageRtxxx.get_supported_memory_types(family, revision) - sch_cfg["keyblob"]["properties"]["keyblob"][ - "template_title" - ] = "Bootable Image blocks definition" - ret = [] - for item in ["family_rev", "keyblob", "fcb", "image_version", "keystore", "application"]: - ret.append(sch_cfg[item]) - return ret - - def image_info(self) -> BinaryImage: - """Create Binary image of bootable image. + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :return: BinaryImage object of bootable image. - """ - bin_image = BinaryImage( - name=f"Bootable Image for {self.family}", size=0, pattern=BinaryPattern("zeros") - ) - if self.keyblob: - bin_image.add_image( - BinaryImage( - name="Key Blob", - size=self.bimg_descr["keyblob_len"], - offset=self.bimg_descr["keyblob_offset"], - binary=self.keyblob, - parent=bin_image, - ) - ) - if self.fcb: - bin_image.add_image( - BinaryImage( - name="FCB", - size=self.bimg_descr["fcb_len"], - offset=self.bimg_descr["fcb_offset"], - binary=self.fcb.export(), - parent=bin_image, - ) - ) - if self.image_version: - bin_image.add_image( - BinaryImage( - name="Image version", - size=self.bimg_descr["image_version_len"], - offset=self.bimg_descr["image_version_offset"], - description=f"Image version is {self.image_version}", - binary=self.image_version.to_bytes(4, "little"), - parent=bin_image, - ) - ) - if self.keystore: - bin_image.add_image( - BinaryImage( - name="Key Store", - size=self.bimg_descr["keystore_len"], - offset=self.bimg_descr["keystore_offset"], - binary=self.keystore, - parent=bin_image, - ) - ) - if self.app: - bin_image.add_image( - BinaryImage( - name="Application", - offset=self.bimg_descr["application_offset"], - binary=self.app, - parent=bin_image, - ) - ) - - return bin_image - - @classmethod - def load_from_config( - cls, config: Dict, search_paths: Optional[List[str]] = None - ) -> "BootableImageRtxxx": - """Load bootable image from configuration. - - :param config: Configuration of Bootable image. - :param search_paths: List of paths where to search for the file, defaults to None + :param mem_type: Used memory type. """ - check_config(config, cls.get_validation_schemas_family()) - chip_family = config["family"] - mem_type = config["memory_type"] - revision = config.get("revision", "latest") - schemas = cls.get_validation_schemas(chip_family, revision) - check_config(config, schemas, search_paths=search_paths) - keyblob_path = config.get("keyblob") - fcb_path = config.get("fcb") - image_version = config.get("image_version", 0) - keystore_path = config.get("keystore") - app_path = config.get("application") - keyblob = load_binary(keyblob_path, search_paths=search_paths) if keyblob_path else None - fcb = load_binary(fcb_path, search_paths=search_paths) if fcb_path else None - keystore = load_binary(keystore_path, search_paths=search_paths) if keystore_path else None - app = load_binary(app_path, search_paths=search_paths) if app_path else None - - return BootableImageRtxxx( - family=chip_family, - mem_type=mem_type, - revision=revision, - keyblob=keyblob, - fcb=fcb, - image_version=image_version, - keystore=keystore, - app=app, - ) + return [ + EnumBimgSegments.KEYBLOB, + EnumBimgSegments.FCB, + EnumBimgSegments.IMAGE_VERSION, + EnumBimgSegments.KEYSTORE, + EnumBimgSegments.APPLICATION, + ] def parse(self, binary: bytes) -> None: """Parse binary into bootable image object. @@ -383,60 +430,34 @@ def parse(self, binary: bytes) -> None: # KeyBlob if start_block_offset == 0: offset = self.bimg_descr["keyblob_offset"] - size = self.bimg_descr["keyblob_len"] - self.keyblob = binary[offset : offset + size] + self.keyblob = binary[offset : offset + self.get_segment_size(EnumBimgSegments.KEYBLOB)] else: self.keyblob = None # FCB offset = self.bimg_descr["fcb_offset"] - start_block_offset - size = self.bimg_descr["fcb_len"] - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(binary[offset : offset + size]) + self.fcb = binary[offset : offset + self.get_segment_size(EnumBimgSegments.FCB)] + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) # Image version offset = self.bimg_descr["image_version_offset"] - start_block_offset - size = self.bimg_descr["image_version_len"] - self.image_version = int.from_bytes(binary[offset : offset + size], "little") + self.image_version = binary[ + offset : offset + self.get_segment_size(EnumBimgSegments.IMAGE_VERSION) + ] # KeyStore offset = self.bimg_descr["keystore_offset"] - start_block_offset - size = self.bimg_descr["keystore_len"] - self.keystore = binary[offset : offset + size] + self.keystore = binary[offset : offset + self.get_segment_size(EnumBimgSegments.KEYSTORE)] # application offset = self.bimg_descr["application_offset"] - start_block_offset - self.app = binary[offset:] + self.application = binary[offset:] def store_config(self, output: str) -> None: """Store bootable image into configuration and binary blocks. :param output: Path to output folder to store bootable image configuration. """ - schemas = self._get_validation_schemas() - override: Dict[str, Union[str, int]] = {} - override["family"] = self.family - override["revision"] = self.revision - override["memory_type"] = self.mem_type - override["image_version"] = self.image_version - override["keyblob"] = "keyblob.bin" if self.keyblob else "" - override["fcb"] = "fcb.bin" if self.fcb else "" - override["keystore"] = "keystore.bin" if self.keystore else "" - override["application"] = "application.bin" if self.app else "" - config = ConfigTemplate( - f"Bootable Image Configuration for {self.family}.", - schemas, - override, - ).export_to_yaml() - write_file( - config, - os.path.join(output, f"bootable_image_{self.family}_{self.mem_type}.yaml"), - ) - if self.keyblob: - write_file(self.keyblob, os.path.join(output, "keyblob.bin"), mode="wb") - if self.fcb: - write_file(self.fcb.export(), os.path.join(output, "fcb.bin"), mode="wb") - write_file(self.fcb.create_config(), os.path.join(output, "fcb.yaml")) - if self.keystore: - write_file(self.keystore, os.path.join(output, "keystore.bin"), mode="wb") - if self.app: - write_file(self.app, os.path.join(output, "application.bin"), mode="wb") + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) @staticmethod def get_supported_families() -> List[str]: @@ -452,14 +473,16 @@ def get_supported_families() -> List[str]: class BootableImageLpc55s3x(BootableImage): """Bootable Image class for LPC55S3x devices.""" + IMAGE_PATTERN = BinaryPattern("ones") + def __init__( self, family: str, mem_type: str = "flexspi_nor", revision: str = "latest", fcb: Optional[bytes] = None, - image_version: int = 0, - app: Optional[bytes] = None, + image_version: Optional[int] = None, + application: Optional[bytes] = None, ) -> None: """Bootable Image constructor for Lpc55s3x devices. @@ -470,110 +493,27 @@ def __init__( """ assert mem_type == "flexspi_nor" super().__init__(family, mem_type, revision) - self.fcb = None - if fcb: - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(fcb) - - self.image_version = image_version - self.app = app - - @staticmethod - def get_validation_schemas(family: str, revision: str = "latest") -> List[Dict[str, Any]]: - """Get validation schema for the family. - - :param family: Chip family - :param revision: Chip revision specification, as default, latest is used. - :return: List of validation schema dictionaries. - """ - ret = [] - sch_cfg = deepcopy(ValidationSchemas.get_schema_file(BIMG_SCH_FILE)) - sch_cfg["family_rev"]["properties"]["family"][ - "enum" - ] = BootableImageLpc55s3x.get_supported_families() - revisions = ["latest"] - revisions_device = BootableImageLpc55s3x.get_supported_revisions(family) - revisions.extend(revisions_device) - sch_cfg["family_rev"]["properties"]["revision"]["enum"] = revisions - mem_types = BootableImageLpc55s3x.get_supported_memory_types(family, revision) - sch_cfg["family_rev"]["properties"]["memory_type"]["enum"] = mem_types - - ret.append(sch_cfg["family_rev"]) - ret.append(sch_cfg["fcb"]) - ret.append(sch_cfg["image_version"]) - ret.append(sch_cfg["application"]) - return ret - - def image_info(self) -> BinaryImage: - """Create Binary image of bootable image. - - :return: BinaryImage object of bootable image. - """ - bin_image = BinaryImage( - name=f"Bootable Image for {self.family}", size=0, pattern=BinaryPattern("ones") - ) + self.fcb = fcb if self.fcb: - bin_image.add_image( - BinaryImage( - name="FCB", - size=self.bimg_descr["fcb_len"], - offset=self.bimg_descr["fcb_offset"], - binary=self.fcb.export(), - parent=bin_image, - ) - ) - if self.image_version: - data = self.image_version & 0xFFFF - data |= (data ^ 0xFFFF) << 16 - bin_image.add_image( - BinaryImage( - name="Image version", - size=self.bimg_descr["image_version_len"], - offset=self.bimg_descr["image_version_offset"], - description=f"Image version is {self.image_version}", - binary=data.to_bytes(4, "little"), - parent=bin_image, - ) - ) - if self.app: - bin_image.add_image( - BinaryImage( - name="Application", - offset=self.bimg_descr["application_offset"], - binary=self.app, - parent=bin_image, - ) - ) + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) - return bin_image + image_version = (image_version or 0) & 0xFFFF + image_version |= (image_version ^ 0xFFFF) << 16 + self.image_version = image_version.to_bytes(4, "little") + self.application = application - @classmethod - def load_from_config( - cls, config: Dict, search_paths: Optional[List[str]] = None - ) -> "BootableImageLpc55s3x": - """Load bootable image from configuration. + @staticmethod + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :param config: Configuration of Bootable image. - :param search_paths: List of paths where to search for the file, defaults to None + :param mem_type: Used memory type. """ - check_config(config, cls.get_validation_schemas_family()) - chip_family = config["family"] - revision = config.get("revision", "latest") - schemas = cls.get_validation_schemas(chip_family, revision) - check_config(config, schemas, search_paths=search_paths) - fcb_path = config.get("fcb") - image_version = config.get("image_version", 0) - app_path = config.get("application") - fcb = load_binary(fcb_path, search_paths=search_paths) if fcb_path else None - app = load_binary(app_path, search_paths=search_paths) if app_path else None - - return BootableImageLpc55s3x( - family=chip_family, - revision=revision, - fcb=fcb, - image_version=image_version, - app=app, - ) + return [ + EnumBimgSegments.FCB, + EnumBimgSegments.IMAGE_VERSION, + EnumBimgSegments.APPLICATION, + ] def parse(self, binary: bytes) -> None: """Parse binary into bootable image object. @@ -590,49 +530,32 @@ def parse(self, binary: bytes) -> None: # FCB offset = self.bimg_descr["fcb_offset"] - start_block_offset - size = self.bimg_descr["fcb_len"] - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(binary[offset : offset + size]) + self.fcb = binary[offset : offset + self.get_segment_size(EnumBimgSegments.FCB)] + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) # Image version offset = self.bimg_descr["image_version_offset"] - start_block_offset - size = self.bimg_descr["image_version_len"] - image_version = int.from_bytes(binary[offset : offset + size], "little") + image_version = int.from_bytes( + binary[offset : offset + self.get_segment_size(EnumBimgSegments.IMAGE_VERSION)], + "little", + ) if image_version != 0xFFFFFFFF and ( image_version & 0xFFFF != ((image_version >> 16) ^ 0xFFFF) & 0xFFFF ): raise SPSDKValueError("Invalid Image version loaded during parse of bootable image.") - self.image_version = image_version & 0xFFFF + self.image_version = (image_version & 0xFFFF).to_bytes(4, "little") # application offset = self.bimg_descr["application_offset"] - start_block_offset - self.app = binary[offset:] + self.application = binary[offset:] def store_config(self, output: str) -> None: """Store bootable image into configuration and binary blocks. :param output: Path to output folder to store bootable image configuration. """ - schemas = self._get_validation_schemas() - override: Dict[str, Union[str, int]] = {} - override["family"] = self.family - override["revision"] = self.revision - override["memory_type"] = self.mem_type - override["image_version"] = self.image_version - override["fcb"] = "fcb.bin" if self.fcb else "" - override["application"] = "application.bin" if self.app else "" - config = ConfigTemplate( - f"Bootable Image Configuration for {self.family}.", - schemas, - override, - ).export_to_yaml() - write_file( - config, - os.path.join(output, f"bootable_image_{self.family}_{self.mem_type}.yaml"), - ) - if self.fcb: - write_file(self.fcb.export(), os.path.join(output, "fcb.bin"), mode="wb") - write_file(self.fcb.create_config(), os.path.join(output, "fcb.yaml")) - if self.app: - write_file(self.app, os.path.join(output, "application.bin"), mode="wb") + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) @staticmethod def get_supported_families() -> List[str]: @@ -645,558 +568,218 @@ def get_supported_families() -> List[str]: return [x for x in full_list if re.match(r"[lL][pP][cC]55[sS]3[\dxX]$", x)] -class BootImgRtSegment(abc.ABC): - """Base class for BootImgRT segment .""" - - def __init__(self, boot_image: BootImgRT) -> None: - """Base class constructor.""" - self.boot_image = boot_image - self.segment: Any = None - - @abc.abstractmethod - def set_value(self, data: bytes) -> None: - """Set value abstract method. - - :param data: Bytes data to set. - """ - raise NotImplementedError() - - @property - @abc.abstractmethod - def is_defined(self) -> bool: - """Is defined abstract property.""" - raise NotImplementedError() - - @property - def offset(self) -> int: - """Offset abstract property.""" - raise SPSDKValueError("Offset not defined") - - @property - def size(self) -> int: - """Get the size of the block.""" - return getattr(self.segment, "size") - - def export(self) -> bytes: - """Export data of given block as bytes object.""" - export = getattr(self.segment, "export") - return export()[: self.size] - - -class BootImgRtFcbSegment(BootImgRtSegment): - """Wrapper of FCB BootImgRT segment .""" - - def __init__(self, boot_image: BootImgRT, segments_config: Dict) -> None: - """FCB BootImgRT segment constructor. - - :param boot_image: Instance of BootImgRT. - :param segments_config: Additional configuration of segments - """ - super().__init__(boot_image) - self.segments_config = segments_config - self.segment: AbstractFCB = self.boot_image.fcb - - def set_value(self, data: bytes) -> None: - """Set value of FCB segment. - - :param data: Bytes data to set. - """ - if data[:4] == FlexSPIConfBlockFCB.TAG: - self.boot_image.fcb = FlexSPIConfBlockFCB.parse(data) - else: - fcb_len = len(data) if len(data) > 0 else BootImgRT.IVT_OFFSET_OTHER - self.boot_image.fcb = PaddingFCB(fcb_len, enabled=True) - - @property - def offset(self) -> int: - """Get the offset of FCB block.""" - fcb_offset = self.segments_config.get("fcb_offset") - if fcb_offset: - return fcb_offset - return self.boot_image.FCB_OFFSETS[0] - - @property - def is_defined(self) -> bool: - """Returns true if FCB block is defined. False otherwise.""" - return self.boot_image.fcb.size > 0 - - def export(self) -> bytes: - """Export data of given block as bytes object.""" - data = self.boot_image.export_fcb(DebugInfo.disabled())[: self.size] - return data - - -class BootImgRtBeeSegment(BootImgRtSegment): - """Wrapper of BEE BootImgRT segment .""" - - def __init__(self, boot_image: BootImgRT) -> None: - """BEE BootImgRT segment constructor. - - :param boot_image: Instance of BootImgRT. - """ - super().__init__(boot_image) - self.segment: SegBEE = self.boot_image.bee +class BootableImageRt101x(BootableImage): + """Bootable Image class for RT11x devices.""" - def set_value(self, data: bytes) -> None: - """Set value of BEE segment. - - :param data: Bytes data to set. - """ - self.boot_image.bee = SegBEE.parse(data) - - @property - def offset(self) -> int: - """Get the offset of BEE block.""" - return BootImgRT.BEE_OFFSET - - @property - def is_defined(self) -> bool: - """Returns true if BEE block is defined. False otherwise.""" - return self.boot_image.bee.size > 0 - - def export(self) -> bytes: - """Export data of given block as bytes object.""" - data = self.boot_image.export_bee(DebugInfo.disabled())[: self.size] - return data - - -class BootImgRtIvtSegment(BootImgRtSegment): - """Wrapper of IVT BootImgRT segment .""" - - def __init__(self, boot_image: BootImgRT) -> None: - """IVT BootImgRT segment constructor. - - :param boot_image: Instance of BootImgRT. - """ - super().__init__(boot_image) - self.segment: SegIVT2 = self.boot_image.ivt - - def set_value(self, data: bytes) -> None: - """Set value of IVT segment. - - :param data: Bytes data to set. - """ - self.boot_image.ivt = SegIVT2.parse(data) - - @property - def offset(self) -> int: - """Get the offset of IVT block.""" - return BootImgRT.IVT_OFFSET_NOR_FLASH - - @property - def is_defined(self) -> bool: - """Returns true if IVT block is defined. False otherwise.""" - return self.boot_image.ivt.size > 0 - - -class BootImgRtBdiSegment(BootImgRtSegment): - """Wrapper of BDI BootImgRT segment .""" - - def __init__(self, boot_image: BootImgRT) -> None: - """BDI BootImgRT segment constructor. - - :param boot_image: Instance of BootImgRT. - """ - super().__init__(boot_image) - self.segment: SegBDT = self.boot_image.bdt - - def set_value(self, data: bytes) -> None: - """Set value of Bdi segment. + def __init__( + self, + family: str, + mem_type: str, + revision: str = "latest", + keyblob: Optional[bytes] = None, + fcb: Optional[bytes] = None, + hab_container: Optional[bytes] = None, + ) -> None: + """Bootable Image constructor for RT1xxx devices. - :param data: Bytes data to set. + :param family: Chip family. + :param mem_type: Used memory type. + :param revision: Chip silicon revision. + :param keyblob: BEE encryption header 0 + :param fcb: FCB block, defaults to None + :param hab_container: Boot image container """ - self.boot_image.bdt = SegBDT.parse(data) - - @property - def offset(self) -> int: - """Get the offset of IVT block.""" - return ( - self.boot_image.ivt.bdt_address - - self.boot_image.ivt.ivt_address - + self.boot_image.ivt_offset - ) - - @property - def is_defined(self) -> bool: - """Returns true if BDI block is defined. False otherwise.""" - return self.boot_image.bdt.size > 0 - - -class BootImgRtDcdSegment(BootImgRtSegment): - """Wrapper of DCD BootImgRT segment .""" + super().__init__(family, mem_type, revision) + self.keyblob = keyblob + self.fcb = fcb + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) + self.hab_container = hab_container - def __init__(self, boot_image: BootImgRT) -> None: - """DCD BootImgRT segment constructor. + @staticmethod + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :param boot_image: Instance of BootImgRT. + :param mem_type: Used memory type. """ - super().__init__(boot_image) - self.segment: Optional[SegDCD] = self.boot_image.dcd + return [ + EnumBimgSegments.KEYBLOB, + EnumBimgSegments.FCB, + EnumBimgSegments.HAB_CONTAINER, + ] - def set_value(self, data: bytes) -> None: - """Set value of Dcd segment. + def parse(self, binary: bytes) -> None: + """Parse binary into bootable image object. - :param data: Bytes data to set. + :param binary: Full binary of bootable image. """ - self.boot_image.dcd = SegDCD.parse(data) - - @property - def offset(self) -> int: - """Get the offset of DCD block.""" - return ( - self.boot_image.ivt.dcd_address - - self.boot_image.ivt.ivt_address - + self.boot_image.ivt_offset - ) - - @property - def is_defined(self) -> bool: - """Returns true if block is defined. False otherwise.""" - return self.boot_image.dcd is not None and self.boot_image.dcd.size > 0 - - def export(self) -> bytes: - """Export data of given block as bytes object.""" - data = self.boot_image.export_dcd(DebugInfo.disabled())[: self.size] - return data - - -class BootImgRtAppSegment(BootImgRtSegment): - """Wrapper of App BootImgRT segment .""" + super().parse(binary) + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) - def __init__(self, boot_image: BootImgRT) -> None: - """App BootImgRT segment constructor. + def store_config(self, output: str) -> None: + """Store bootable image into configuration and binary blocks. - :param boot_image: Instance of BootImgRT. + :param output: Path to output folder to store bootable image configuration. """ - super().__init__(boot_image) - self.segment: SegAPP = self.boot_image.app + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) - def set_value(self, data: bytes) -> None: - """Set value of App segment. + @staticmethod + def get_supported_families() -> List[str]: + """Get list of all supported families by bootable image. - :param data: Bytes data to set. + :return: List of families. """ - self.boot_image.app.data = data - - @property - def offset(self) -> int: - """Get the offset of App block.""" - return self.boot_image.app_offset - - @property - def is_defined(self) -> bool: - """Returns True if block is defined. False otherwise.""" - return self.boot_image.app.size > 0 - + return ["rt101x"] -class BootImgRtXmcdSegment(BootImgRtSegment): - """Wrapper of XMCD BootImgRT segment.""" - def __init__(self, boot_image: BootImgRT) -> None: - """XMCD BootImgRT segment constructor. - - :param boot_image: Instance of BootImgRT. - """ - super().__init__(boot_image) - self.segment: Optional[SegXMCD] = self.boot_image.xmcd +class BootableImageRt10xx(BootableImage): + """Bootable Image class for RT1xxx devices.""" - def set_value(self, data: bytes) -> None: - """Set value of XMCD segment. + def __init__( + self, + family: str, + mem_type: str, + revision: str = "latest", + bee_header_0: Optional[bytes] = None, + bee_header_1: Optional[bytes] = None, + fcb: Optional[bytes] = None, + hab_container: Optional[bytes] = None, + ) -> None: + """Bootable Image constructor for RT1xxx devices. - :param data: Bytes data to set. + :param family: Chip family. + :param mem_type: Used memory type. + :param revision: Chip silicon revision. + :param bee_header_0: BEE encryption header 0 + :param bee_header_1: BEE encryption header 1 + :param fcb: FCB block, defaults to None + :param hab_container: Boot image container """ - self.boot_image.xmcd = SegXMCD.parse(data) - - @property - def offset(self) -> int: - """Get the offset of XMCD block.""" - return self.boot_image.ivt_offset + self.boot_image.XMCD_IVT_OFFSET - - @property - def is_defined(self) -> bool: - """Returns True if block is defined. False otherwise.""" - return self.boot_image.xmcd is not None - - def export(self) -> bytes: - """Export data of given block as bytes object.""" - data = b"" - if self.boot_image.xmcd: - data = self.boot_image.xmcd.export() - return data - - -class BootImgRtCsfSegment(BootImgRtSegment): - """Wrapper of CSF BootImgRT segment.""" + super().__init__(family, mem_type, revision) + self.bee_header_0 = bee_header_0 + self.bee_header_1 = bee_header_1 + self.fcb = fcb + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) + self.hab_container = hab_container - def __init__(self, boot_image: BootImgRT) -> None: - """CSF BootImgRT segment constructor. + @staticmethod + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :param boot_image: Instance of BootImgRT. + :param mem_type: Used memory type. """ - super().__init__(boot_image) - self.segment: Optional[SegCSF] = self.boot_image.csf + return [ + EnumBimgSegments.FCB, + EnumBimgSegments.BEE_HEADER_0, + EnumBimgSegments.BEE_HEADER_1, + EnumBimgSegments.HAB_CONTAINER, + ] - def set_value(self, data: bytes) -> None: - """Set value of CSF segment. + def parse(self, binary: bytes) -> None: + """Parse binary into bootable image object. - :param data: Bytes data to set. + :param binary: Full binary of bootable image. """ - self.boot_image.csf = SegCSF.parse(data) - - @property - def offset(self) -> int: - """Get the offset of CSF block.""" - return ( - self.boot_image.ivt.csf_address - - self.boot_image.ivt.ivt_address - + self.boot_image.ivt_offset - ) + super().parse(binary) + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) - @property - def is_defined(self) -> bool: - """Returns True if block is defined. False otherwise.""" - return self.boot_image.csf is not None and self.boot_image.csf.size > 0 + def store_config(self, output: str) -> None: + """Store bootable image into configuration and binary blocks. - def export(self) -> bytes: - """Export data of given block as bytes object.""" - data = b"" - if self.boot_image.csf: - data = self.boot_image.csf.export() - return data - - -class BootImgRTSegmentsFactory: - """Factory class for BootImgRT segments.""" - - BOOT_IMAGE_SEGMENTS: Dict[str, Type[BootImgRtSegment]] = { - "fcb": BootImgRtFcbSegment, - "bee": BootImgRtBeeSegment, - "ivt": BootImgRtIvtSegment, - "bdi": BootImgRtBdiSegment, - "xmcd": BootImgRtXmcdSegment, - "dcd": BootImgRtDcdSegment, - "application": BootImgRtAppSegment, - "csf": BootImgRtCsfSegment, - } - - def __init__(self, boot_image: BootImgRT) -> None: - """Factory class constructor. - - :param boot_image: Instance of BootImgRT. - :param segments_config: Additional configuration of segments. + :param output: Path to output folder to store bootable image configuration. """ - self.boot_image = boot_image + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) - def get_segment(self, name: str, segments_config: Optional[Dict] = None) -> BootImgRtSegment: - """Get instance of segment by given name. + @staticmethod + def get_supported_families() -> List[str]: + """Get list of all supported families by bootable image. - :param name: Name of segment. - :param segments_config: Additional configuration of segments. - :raises SPSDKValueError: If segments_config value is missing for segment with required segments_config parameter - """ - try: - segment = self.BOOT_IMAGE_SEGMENTS[name] - except KeyError: - logger.debug(f"Segment {name} is not recognized as valid segment.") - init_signature = inspect.signature(segment.__init__) - kwargs = {} - if "segments_config" in init_signature.parameters: - if not segments_config: - raise SPSDKValueError(f"Segments config must be speecified for segment {name}") - kwargs["segments_config"] = segments_config - return segment(self.boot_image, **kwargs) - - def get_all_segments( - self, segments_config: Optional[Dict] = None - ) -> Dict[str, BootImgRtSegment]: - """Get instances of all segments. - - :param segments_config: Additional configuration of segments. + :return: List of families. """ - segments = {} - for name in self.BOOT_IMAGE_SEGMENTS: - segments[name] = self.get_segment(name, segments_config) - return segments - - @staticmethod - def get_all_segment_names() -> List[str]: - """Get list of all segment names.""" - return list(BootImgRTSegmentsFactory.BOOT_IMAGE_SEGMENTS.keys()) + ignored = BootableImageRt101x.get_supported_families() + full_list = BootableImage.get_supported_families() + return [x for x in full_list if re.match(r"[rR][tT]10[\dxX]{2}$", x) and x not in ignored] -class BootableImageRt1xxx(BootableImage): +class BootableImageRt11xx(BootableImage): """Bootable Image class for RT1xxx devices.""" - DEFAULT_IMAGE_VERSION = BootImgRT.VERSIONS[0] - def __init__( self, family: str, mem_type: str, revision: str = "latest", - image_version: int = DEFAULT_IMAGE_VERSION, - **segments: bytes, + keyblob: Optional[bytes] = None, + fcb: Optional[bytes] = None, + keystore: Optional[bytes] = None, + hab_container: Optional[bytes] = None, ) -> None: """Bootable Image constructor for RT1xxx devices. :param family: Chip family. :param mem_type: Used memory type. :param revision: Chip silicon revision. - :param image_version: Image version number, defaults to 0 + :param keyblob: Key Blob block, defaults to None + :param fcb: FCB block, defaults to None + :param keystore: Key store block, defaults to None + :param hab_container: Boot image container """ super().__init__(family, mem_type, revision) - self.image_version = image_version - self.boot_image = BootImgRT( - 0, - version=image_version, - ) - self.segments = segments - self.bimg_descr: Dict = self.mem_types[self.mem_type] - self.segment_factory = BootImgRTSegmentsFactory(self.boot_image) - for name, value in segments.items(): - if value is not None: - segment = self.segment_factory.get_segment(name, self.bimg_descr) - segment.set_value(value) - # Construct the FCB object so the yaml can be exported - self.fcb = None - if segments.get("fcb") is not None: - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(segments["fcb"]) + self.keyblob = keyblob + self.fcb = fcb + self.keystore = keystore + self.fcb_obj = None + if self.fcb is not None: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) + self.hab_container = hab_container @staticmethod - def get_validation_schemas(family: str, revision: str = "latest") -> List[Dict[str, Any]]: - """Get validation schema for the family. - - :raises SPSDKKeyError: If given item is not defined in validation schema. - :return: List of validation schema dictionaries. - """ - sch_cfg = deepcopy(ValidationSchemas.get_schema_file(BIMG_SCH_FILE)) - sch_cfg["family_rev"]["properties"]["family"][ - "enum" - ] = BootableImageRt1xxx.get_supported_families() - sch_cfg["family_rev"]["properties"]["revision"][ - "enum" - ] = BootableImageRt1xxx.get_supported_revisions(family) - sch_cfg["family_rev"]["properties"]["memory_type"][ - "enum" - ] = BootableImageRt1xxx.get_supported_memory_types(family, revision) - sch_cfg["fcb"]["properties"]["fcb"]["template_title"] = "Bootable Image blocks definition" - ret = [] - schema_items = [ - "family_rev", - "image_version", - ] + BootImgRTSegmentsFactory.get_all_segment_names() - for item in schema_items: - try: - ret.append(sch_cfg[item]) - except KeyError as e: - raise SPSDKKeyError( - f"Item {item} not defined in validation schema {BIMG_SCH_FILE}" - ) from e - return ret - - def export(self) -> bytes: - """Export bootable image. - - :return: Complete binary of bootable image. - """ - return self.image_info().export() - - def image_info(self) -> BinaryImage: - """Create Binary image of bootable image. - - :return: BinaryImage object of bootable image. - """ - bin_image = BinaryImage( - name=f"Bootable Image for {self.family}", size=0, pattern=BinaryPattern("zeros") - ) - segments = self.segment_factory.get_all_segments(self.bimg_descr) - for name, seg_instance in segments.items(): - if seg_instance.is_defined: - bin_image.add_image( - BinaryImage( - name=name.upper(), - size=seg_instance.size, - offset=seg_instance.offset, - binary=seg_instance.export(), - parent=bin_image, - ) - ) - return bin_image - - @classmethod - def load_from_config( - cls, config: Dict, search_paths: Optional[List[str]] = None - ) -> "BootableImageRt1xxx": - """Load bootable image from configuration. + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :param config: Configuration of Bootable image. - :param search_paths: List of paths where to search for the file, defaults to None + :param mem_type: Used memory type. """ - check_config(config, cls.get_validation_schemas_family()) - revision = config.get("revision", "latest") - schemas = cls.get_validation_schemas(config["family"], revision) - check_config(config, schemas, search_paths=search_paths) - segments: Dict[str, Any] = {} - for name in BootImgRTSegmentsFactory.get_all_segment_names(): - segments[name] = cls._load_bin_from_config(config, name, search_paths) - return BootableImageRt1xxx( - family=config["family"], - mem_type=config["memory_type"], - revision=revision, - image_version=config.get("image_version", cls.DEFAULT_IMAGE_VERSION), - **segments, - ) + segments = [EnumBimgSegments.HAB_CONTAINER] + if mem_type == "flexspi_nor": + segments.extend( + [ + EnumBimgSegments.KEYBLOB, + EnumBimgSegments.FCB, + EnumBimgSegments.KEYSTORE, + ] + ) + return segments def parse(self, binary: bytes) -> None: """Parse binary into bootable image object. - :param binary: Complete binary of bootable image. + :param binary: Full binary of bootable image. """ - self.boot_image = self.boot_image.parse(binary) - self.image_version = self.boot_image.ivt.version - self.boot_image._update() - self.segment_factory = BootImgRTSegmentsFactory(self.boot_image) - debug_disabled = DebugInfo.disabled() - if self.boot_image.fcb.enabled: - data = self.boot_image.export_fcb(debug_disabled) - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(data[: self.boot_image.fcb.size]) + super().parse(binary) + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) def store_config(self, output: str) -> None: """Store bootable image into configuration and binary blocks. :param output: Path to output folder to store bootable image configuration. """ - schemas = self._get_validation_schemas() - override: Dict[str, Union[str, int]] = {} - override["family"] = self.family - override["revision"] = self.revision - override["memory_type"] = self.mem_type - override["image_version"] = self.image_version - segments = self.segment_factory.get_all_segments(self.bimg_descr) - # Write binaries - for name, seg_instance in segments.items(): - override[name] = "" - if seg_instance.is_defined: - override[name] = self.get_validation_schema_template(schemas, name) - write_file( - seg_instance.export(), - os.path.join(output, self.get_validation_schema_template(schemas, name)), - mode="wb", - ) - # Write YAMLs - config = ConfigTemplate( - f"Bootable Image Configuration for {self.family}.", - schemas, - override, - ).export_to_yaml() - write_file( - config, - os.path.join(output, f"bootable_image_{self.family}_{self.mem_type}.yaml"), - ) - if self.fcb: - write_file(self.fcb.create_config(), os.path.join(output, "fcb.yaml")) + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) @staticmethod def get_supported_families() -> List[str]: @@ -1205,26 +788,13 @@ def get_supported_families() -> List[str]: :return: List of families. """ full_list = BootableImage.get_supported_families() - ignored = ["rt118x"] + ignored = BootableImageRt118x.get_supported_families() # filter out just RT1xxx - return [x for x in full_list if re.match(r"[rR][tT]1[\dxX]{3}$", x) and x not in ignored] - - @staticmethod - def get_validation_schema_template(schemas: List[Dict[str, Any]], property_name: str) -> str: - """Get the template value from validation schema for specific. - - :param schemas: Validation schemas. - :param property_name: name of propetry to be searched. - :raises SPSDKKeyError: If property with given name does nto exist. - """ - prop = find_first(schemas, lambda x: property_name in x["properties"]) - if not prop: - raise SPSDKKeyError(f"A property {property_name} is not defined in validation schemas") - return prop["properties"][property_name]["template_value"] + return [x for x in full_list if re.match(r"[rR][tT]11[\dxX]{2}$", x) and x not in ignored] class BootableImageRt118x(BootableImage): - """Bootable Image class for RT1180 devices.""" + """Bootable Image class for RT118x devices.""" def __init__( self, @@ -1246,128 +816,41 @@ def __init__( assert mem_type == "flexspi_nor" super().__init__(family, mem_type, revision) self.keyblob = keyblob - self.fcb = None - if fcb: - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(fcb) - self.xmcd = None - if xmcd: - self.xmcd = XMCD(self.family, self.revision) - self.xmcd.parse(xmcd) + self.fcb = fcb + self.fcb_obj = None + if self.fcb: + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) + self.xmcd = xmcd + self.xmcd_obj = None + if self.xmcd: + self.xmcd_obj = XMCD(self.family, self.revision) + self.xmcd_obj.parse(self.xmcd) self.ahab_container = ahab_container @staticmethod - def get_validation_schemas(family: str, revision: str = "latest") -> List[Dict[str, Any]]: - """Get validation schema for the family. + def get_segments(mem_type: str) -> List[EnumBimgSegments]: + """Get list of image segments. - :param family: Chip family - :param revision: Chip revision specification, as default, latest is used. - :return: List of validation schema dictionaries. - """ - ret = [] - sch_cfg = deepcopy(ValidationSchemas.get_schema_file(BIMG_SCH_FILE)) - sch_cfg["family_rev"]["properties"]["family"][ - "enum" - ] = BootableImageRt118x.get_supported_families() - revisions = ["latest"] - revisions_device = BootableImageRt118x.get_supported_revisions(family) - revisions.extend(revisions_device) - sch_cfg["family_rev"]["properties"]["revision"]["enum"] = revisions - mem_types = BootableImageRt118x.get_supported_memory_types(family, revision) - sch_cfg["family_rev"]["properties"]["memory_type"]["enum"] = mem_types - - ret.append(sch_cfg["family_rev"]) - ret.append(sch_cfg["keyblob"]) - ret.append(sch_cfg["fcb"]) - ret.append(sch_cfg["xmcd"]) - ret.append(sch_cfg["ahab_container"]) - return ret - - def image_info(self) -> BinaryImage: - """Create Binary image of bootable image. - - :return: BinaryImage object of bootable image. + :param mem_type: Used memory type. """ - bin_image = BinaryImage( - name=f"Bootable Image for {self.family}", size=0, pattern=BinaryPattern("zeros") - ) - if self.keyblob: - bin_image.add_image( - BinaryImage( - name="Key Blob", - size=len(self.keyblob), - offset=self.bimg_descr["keyblob_offset"], - binary=self.keyblob, - parent=bin_image, - ) - ) - if self.fcb: - bin_image.add_image( - BinaryImage( - name="FCB", - size=len(self.fcb.registers.image_info()), - offset=self.bimg_descr["fcb_offset"], - binary=self.fcb.export(), - parent=bin_image, - ) - ) - if self.xmcd: - bin_image.add_image( - BinaryImage( - name="XMCD", - size=len(self.xmcd.registers.image_info()), - offset=self.bimg_descr["xmcd_offset"], - binary=self.xmcd.export(), - parent=bin_image, - ) - ) - if self.ahab_container: - bin_image.add_image( - BinaryImage( - name="AHAB Container", - offset=self.bimg_descr["ahab_container_offset"], - binary=self.ahab_container, - parent=bin_image, - ) - ) - return bin_image + return [ + EnumBimgSegments.KEYBLOB, + EnumBimgSegments.FCB, + EnumBimgSegments.XMCD, + EnumBimgSegments.AHAB_CONTAINER, + ] - @classmethod - def load_from_config( - cls, config: Dict, search_paths: Optional[List[str]] = None - ) -> "BootableImageRt118x": - """Load bootable image from configuration. + def store_config(self, output: str) -> None: + """Store bootable image into configuration and binary blocks. - :param config: Configuration of Bootable image. - :param search_paths: List of paths where to search for the file, defaults to None + :param output: Path to output folder to store bootable image configuration. """ - check_config(config, cls.get_validation_schemas_family()) - chip_family = config["family"] - mem_type = config["memory_type"] - revision = config.get("revision", "latest") - schemas = cls.get_validation_schemas(chip_family, revision) - check_config(config, schemas, search_paths=search_paths) - keyblob_path = config.get("keyblob") - fcb_path = config.get("fcb") - xmcd_path = config.get("xmcd") - ahab_container_path = config.get("ahab_container") - keyblob = load_binary(keyblob_path, search_paths=search_paths) if keyblob_path else None - fcb = load_binary(fcb_path, search_paths=search_paths) if fcb_path else None - xmcd = load_binary(xmcd_path, search_paths=search_paths) if xmcd_path else None - ahab_container = ( - load_binary(ahab_container_path, search_paths=search_paths) - if ahab_container_path - else None - ) - return BootableImageRt118x( - family=chip_family, - mem_type=mem_type, - revision=revision, - keyblob=keyblob, - fcb=fcb, - xmcd=xmcd, - ahab_container=ahab_container, - ) + super().store_config(output) + if self.fcb_obj: + write_file(self.fcb_obj.create_config(), os.path.join(output, "fcb.yaml")) + if self.xmcd_obj: + write_file(self.xmcd_obj.create_config(), os.path.join(output, "xmcd.yaml")) def parse(self, binary: bytes) -> None: """Parse binary into bootable image object. @@ -1376,19 +859,20 @@ def parse(self, binary: bytes) -> None: """ # KeyBlob offset = self.bimg_descr["keyblob_offset"] - size = self.bimg_descr["keyblob_len"] - self.keyblob = binary[offset : offset + size] + self.keyblob = binary[offset : offset + self.get_segment_size(EnumBimgSegments.KEYBLOB)] # FCB offset = self.bimg_descr["fcb_offset"] - size = self.bimg_descr["fcb_len"] - self.fcb = FCB(self.family, self.mem_type, self.revision) - self.fcb.parse(binary[offset : offset + size]) + self.fcb = binary[offset : offset + self.get_segment_size(EnumBimgSegments.FCB)] + self.fcb_obj = FCB(self.family, self.mem_type, self.revision) + self.fcb_obj.parse(self.fcb) # XMCD offset = self.bimg_descr["xmcd_offset"] size = self._get_xmcd_size(binary[offset : offset + XMCDHeader.SIZE]) + self.xmcd_obj if size > 0: - self.xmcd = XMCD(self.family, self.revision) - self.xmcd.parse(binary[offset:size]) + self.xmcd = binary[offset:size] + self.xmcd_obj = XMCD(self.family, self.revision) + self.xmcd_obj.parse(self.xmcd) # AHAB container offset = self.bimg_descr["ahab_container_offset"] self.ahab_container = binary[offset:] @@ -1396,49 +880,13 @@ def parse(self, binary: bytes) -> None: def _get_xmcd_size(self, header_binary: bytes) -> int: try: header = XMCDHeader.parse(header_binary) - except UnparsedException: + except SPSDKParsingError: return 0 mem_type = MemoryType(header.interface).name.lower() config_type = ConfigurationBlockType(header.block_type).name.lower() registers = XMCD.load_registers(self.family, mem_type, config_type, self.revision) return len(registers.image_info()) - def store_config(self, output: str) -> None: - """Store bootable image into configuration and binary blocks. - - :param output: Path to output folder to store bootable image configuration. - """ - schemas = self._get_validation_schemas() - override: Dict[str, str] = {} - override["family"] = self.family - override["revision"] = self.revision - override["memory_type"] = self.mem_type - override["keyblob"] = "keyblob.bin" if self.keyblob else "" - override["fcb"] = "fcb.bin" if self.fcb else "" - override["xmcd"] = "xmcd.bin" if self.xmcd else "" - override["ahab_container"] = "ahab_container.bin" if self.ahab_container else "" - config = ConfigTemplate( - f"Bootable Image Configuration for {self.family}.", - schemas, - override, - ).export_to_yaml() - write_file( - config, - os.path.join(output, f"bootable_image_{self.family}_{self.mem_type}.yaml"), - ) - if self.keyblob: - write_file(self.keyblob, os.path.join(output, "keyblob.bin"), mode="wb") - if self.fcb: - write_file(self.fcb.export(), os.path.join(output, override["fcb"]), mode="wb") - write_file(self.fcb.create_config(), os.path.join(output, "fcb.yaml")) - if self.xmcd: - write_file(self.xmcd.export(), os.path.join(output, override["xmcd"]), mode="wb") - write_file(self.xmcd.create_config(), os.path.join(output, "xmcd.yaml")) - if self.ahab_container: - write_file( - self.ahab_container, os.path.join(output, override["ahab_container"]), mode="wb" - ) - @staticmethod def get_supported_families() -> List[str]: """Get list of all supported families by bootable image. diff --git a/spsdk/image/commands.py b/spsdk/image/commands.py index 1fbd0f43..aaaf63f2 100644 --- a/spsdk/image/commands.py +++ b/spsdk/image/commands.py @@ -17,6 +17,7 @@ from spsdk import SPSDKError from spsdk.crypto import Certificate, Encoding +from spsdk.exceptions import SPSDKValueError from spsdk.utils.crypto import crypto_backend, matches_key_and_cert from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import DebugInfo @@ -267,7 +268,7 @@ def ops(self) -> int: @ops.setter def ops(self, value: int) -> None: if value not in EnumWriteOps: - raise SPSDKError + raise SPSDKValueError("Value not defined") self._header.param &= ~(0x3 << 3) self._header.param |= int(value) << 3 @@ -1342,14 +1343,14 @@ def cmd_data_reference(self, value: SignatureOrMAC) -> None: By default, the command does not support cmd_data_reference :param value: to be set - :raise ExpectedSignatureOrMACError: if unsupported data object is provided + :raises ExpectedSignatureOrMACError: if unsupported data object is provided """ if self.sig_format == EnumCertFormat.AEAD: assert isinstance(value, MAC) elif self.sig_format == EnumCertFormat.CMS: assert isinstance(value, Signature) else: - raise ExpectedSignatureOrMACError() + raise ExpectedSignatureOrMACError("Unsupported data object is provided") self._signature = value def parse_cmd_data(self, data: bytes, offset: int) -> SignatureOrMAC: @@ -1358,7 +1359,7 @@ def parse_cmd_data(self, data: bytes, offset: int) -> SignatureOrMAC: :param data: to be parsed :param offset: start position in data to parse :return: parsed data object; command-specific: Signature or MAC - :raise ExpectedSignatureOrMACError: if unsupported data object is provided + :raises ExpectedSignatureOrMACError: if unsupported data object is provided """ header = Header.parse(data, offset) if header.tag == SegTag.MAC: diff --git a/spsdk/image/fcb/__init__.py b/spsdk/image/fcb/__init__.py index 5987941c..83553ad9 100644 --- a/spsdk/image/fcb/__init__.py +++ b/spsdk/image/fcb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -11,5 +11,5 @@ from spsdk import SPSDK_DATA_FOLDER FCB_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "image", "fcb") -FCB_SCH_FILE: str = os.path.join(FCB_DATA_FOLDER, "sch_fcb.yml") -FCB_DATABASE_FILE: str = os.path.join(FCB_DATA_FOLDER, "database.yml") +FCB_SCH_FILE: str = os.path.join(FCB_DATA_FOLDER, "sch_fcb.yaml") +FCB_DATABASE_FILE: str = os.path.join(FCB_DATA_FOLDER, "database.yaml") diff --git a/spsdk/image/hab/__init__.py b/spsdk/image/hab/__init__.py new file mode 100644 index 00000000..e85087a5 --- /dev/null +++ b/spsdk/image/hab/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""This module contains HAB related code.""" diff --git a/spsdk/image/hab/hab_container.py b/spsdk/image/hab/hab_container.py new file mode 100644 index 00000000..efe5f06a --- /dev/null +++ b/spsdk/image/hab/hab_container.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""This module contains HAB related code.""" + +import logging +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict, List, Optional, Type + +from spsdk.exceptions import SPSDKKeyError +from spsdk.image import segments +from spsdk.image.images import BootImgRT +from spsdk.utils.images import BinaryImage +from spsdk.utils.misc import BinaryPattern, find_file, find_first, load_binary + +logger = logging.getLogger(__name__) + + +class HabEnumSegments(str, Enum): + """Enum definition for 'par' parameter of Check Data command.""" + + IVT = "ivt" + BDT = "bdt" + DCD = "dcd" + XMCD = "xmcd" + APP = "app" + + +@dataclass +class ConfigOptions: + """Dataclass holding data of options section of BD config file.""" + + flags: int + start_address: int + ivt_offset: int + initial_load_size: int + entrypoint_address: int + dcd_file_path: Optional[str] = None + xmcd_file_path: Optional[str] = None + + @staticmethod + def parse(options: Dict[str, Any]) -> "ConfigOptions": + """Parse config options from dictionary. + + :param options: Optiona sto be parsed + """ + return ConfigOptions( + flags=options["flags"], + start_address=options["startAddress"], + ivt_offset=options["ivtOffset"], + initial_load_size=options["initialLoadSize"], + entrypoint_address=options["entryPointAddress"], + dcd_file_path=options.get("DCDFilePath"), + xmcd_file_path=options.get("XMCDFilePath"), + ) + + +class HabContainer: + """Hab container.""" + + IVT_VERSION = 0x40 + + def __init__(self, binary_image: BinaryImage) -> None: + """HAB Constructor. + + :param binary_image: Binary image with required segments. + """ + self.binary_image = binary_image + + @property + def ivt_segment(self) -> Optional[bytes]: + """IVT segment binary.""" + return self._get_segment_binary(HabEnumSegments.IVT) + + @property + def bdt_segment(self) -> Optional[bytes]: + """BDT segment binary.""" + return self._get_segment_binary(HabEnumSegments.BDT) + + @property + def dcd_segment(self) -> Optional[bytes]: + """DCD segment binary.""" + return self._get_segment_binary(HabEnumSegments.DCD) + + @property + def xmcd_segment(self) -> Optional[bytes]: + """XMCD segment binary.""" + return self._get_segment_binary(HabEnumSegments.XMCD) + + @property + def app_segment(self) -> Optional[bytes]: + """APP segment binary.""" + return self._get_segment_binary(HabEnumSegments.APP) + + def _get_segment_binary(self, segment: HabEnumSegments) -> Optional[bytes]: + """Get segment by name. + + :param segment: Segment to be found + """ + seg = find_first(self.binary_image.sub_images, lambda x: x.name == segment.value) + return seg.binary if seg else None + + @classmethod + def load( + cls, bd_data: Dict[str, Any], external: List, search_paths: Optional[List[str]] = None + ) -> "HabContainer": + """Load the HAB container object from parsed bd_data configuration. + + :param bd_data: Dictionary of the command file content + :param external: List of external files + :param search_paths: List of paths where to search for the file, defaults to None + """ + options = ConfigOptions.parse(bd_data["options"]) + bin_image = BinaryImage(name=f"HAB", size=0, pattern=BinaryPattern("zeros")) + # TODO: Fix lexer so the externals are parsed into bd_data + app_bin = BinaryImage.load_binary_image(external[0], search_paths=search_paths).export() + # IVT + ivt = segments.SegIVT2(HabContainer.IVT_VERSION) + ivt.app_address = options.entrypoint_address + ivt.ivt_address = options.start_address + options.ivt_offset + ivt.bdt_address = options.start_address + options.ivt_offset + ivt.size + ivt_image = BinaryImage( + name=HabEnumSegments.IVT.value, + size=len(ivt.export()), + offset=0, + binary=ivt.export(), + parent=bin_image, + ) + bin_image.add_image(ivt_image) + # BDT + image_len = options.initial_load_size + len(app_bin) + bdt = segments.SegBDT(app_start=options.start_address, app_length=image_len) + bdt_image = BinaryImage( + name=HabEnumSegments.BDT.value, + size=len(bdt.export()), + offset=ivt_image.offset + ivt.bdt_address - ivt.ivt_address, + binary=bdt.export(), + parent=bin_image, + ) + bin_image.add_image(bdt_image) + # DCD + if options.dcd_file_path is not None: + dcd_path = find_file(options.dcd_file_path, search_paths=search_paths) + dcd_bin = load_binary(dcd_path) + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.DCD.value, + size=len(dcd_bin), + offset=bdt_image.offset + len(bdt_image), + binary=dcd_bin, + parent=bin_image, + ) + ) + # XMCD + if options.xmcd_file_path is not None: + xmcd_path = find_file(options.xmcd_file_path, search_paths=search_paths) + xmcd_bin = load_binary(xmcd_path) + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.XMCD.value, + size=len(xmcd_bin), + offset=ivt_image.offset + BootImgRT.XMCD_IVT_OFFSET, + binary=xmcd_bin, + parent=bin_image, + ) + ) + # APP + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.APP.value, + size=len(app_bin), + offset=options.initial_load_size - options.ivt_offset, + binary=app_bin, + parent=bin_image, + ) + ) + return HabContainer(binary_image=bin_image) + + @classmethod + def parse(cls, binary: bytes) -> "HabContainer": + """Parse existing binary into HAB container object. + + :param binary:Binary to be parsed + """ + rt = BootImgRT.parse(binary) + # IVT + bin_image = BinaryImage(name=f"HAB", size=0, pattern=BinaryPattern("zeros")) + ivt_image = BinaryImage( + name=HabEnumSegments.IVT.value, + size=len(rt.ivt.export()), + offset=rt.offset - rt.ivt_offset, + binary=rt.ivt.export(), + parent=bin_image, + ) + bin_image.add_image(ivt_image) + # BDT + if rt.bdt is not None: + bdt_image = BinaryImage( + name=HabEnumSegments.BDT.value, + size=len(rt.bdt.export()), + offset=ivt_image.offset + rt.ivt.bdt_address - rt.ivt.ivt_address, + binary=rt.bdt.export(), + parent=bin_image, + ) + bin_image.add_image(bdt_image) + # DCD + if rt.dcd is not None: + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.DCD.value, + size=len(rt.dcd.export()), + offset=ivt_image.offset + rt.ivt.dcd_address - rt.ivt.ivt_address, + binary=rt.dcd.export(), + parent=bin_image, + ) + ) + # XMCD + if rt.xmcd is not None: + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.XMCD.value, + size=len(rt.xmcd.export()), + offset=ivt_image.offset + BootImgRT.XMCD_IVT_OFFSET, + binary=rt.xmcd.export(), + parent=bin_image, + ) + ) + if rt.app is not None: + bin_image.add_image( + BinaryImage( + name=HabEnumSegments.APP.value, + size=len(rt.app.export()), + offset=ivt_image.offset + rt.app_offset - rt.ivt_offset, + binary=rt.app.export(), + parent=bin_image, + ) + ) + return HabContainer(bin_image) + + def export(self) -> bytes: + """Export into binary.""" + return self.binary_image.export() diff --git a/spsdk/image/header.py b/spsdk/image/header.py index f68de5e7..5ede1408 100644 --- a/spsdk/image/header.py +++ b/spsdk/image/header.py @@ -12,6 +12,7 @@ from typing import Optional from spsdk import SPSDKError +from spsdk.exceptions import SPSDKParsingError from spsdk.utils.easy_enum import Enum ######################################################################################################################## @@ -53,19 +54,6 @@ class CmdTag(Enum): UNLK = (0xB2, "UNLK", "Unlock") -######################################################################################################################## -# Exceptions -######################################################################################################################## - - -class UnparsedException(Exception): - """Unparsed Exception.""" - - -class CorruptedException(Exception): - """Corrupted Exception.""" - - ######################################################################################################################## # Classes ######################################################################################################################## @@ -137,11 +125,11 @@ def parse(cls, data: bytes, offset: int = 0, required_tag: Optional[int] = None) :param offset: Offset of input data :param required_tag: Check header TAG if specified value or ignore if is None :return: Header object - :raise UnparsedException: if required header tag does not match + :raises SPSDKParsingError: if required header tag does not match """ tag, length, param = unpack_from(cls.FORMAT, data, offset) if required_tag is not None and tag != required_tag: - raise UnparsedException( + raise SPSDKParsingError( f" Invalid header tag: '0x{tag:02X}' expected '0x{required_tag:02X}' " ) @@ -176,7 +164,7 @@ def parse(cls, data: bytes, offset: int = 0, required_tag: Optional[int] = None) :param offset: to start reading binary data :param required_tag: CmdTag, None if not required :return: parsed instance - :raises UnparsedException: if required header tag does not match + :raises SPSDKParsingError: If required header tag does not match :raises SPSDKError: If invalid tag """ if required_tag is not None: @@ -201,12 +189,12 @@ def parse(cls, data: bytes, offset: int = 0, required_tag: Optional[int] = None) :param data: Raw data as bytes or bytearray :param offset: Offset of input data :param required_tag: Check header TAG if specified value or ignore if is None - :raises UnparsedException: Raises an error if required tag is empty or not valid + :raises SPSDKParsingError: Raises an error if required tag is empty or not valid :return: Header2 object """ param, length, tag = unpack_from(cls.FORMAT, data, offset) if required_tag is not None and tag != required_tag: - raise UnparsedException( + raise SPSDKParsingError( f" Invalid header tag: '0x{tag:02X}' expected '0x{required_tag:02X}' " ) diff --git a/spsdk/image/images.py b/spsdk/image/images.py index 39b1e19e..2c5ba101 100644 --- a/spsdk/image/images.py +++ b/spsdk/image/images.py @@ -18,7 +18,7 @@ from cryptography.hazmat.primitives.ciphers.aead import AESCCM from cryptography.hazmat.primitives.serialization import Encoding -from spsdk import SPSDKError +from spsdk.exceptions import SPSDKError, SPSDKParsingError from spsdk.utils.crypto import crypto_backend from spsdk.utils.easy_enum import Enum from spsdk.utils.misc import DebugInfo, align, align_block, extend_block @@ -32,7 +32,7 @@ EnumEngine, EnumInsKey, ) -from .header import Header, Header2, UnparsedException +from .header import Header, Header2 from .misc import NotEnoughBytesException, read_raw_data, read_raw_segment from .secret import MAC, CertificateImg, Signature, SrkTable from .segments import ( @@ -146,6 +146,7 @@ def parse( # Boot Image V2 (i.MX-RT) ######################################################################################################################## + # pylint: disable=too-many-public-methods class BootImgRT(BootImgBase): """IMX Boot Image v2.""" @@ -378,12 +379,23 @@ def bee(self, bee: SegBEE) -> None: def app_offset(self) -> int: """:return: offset in the binary image, where the application starts. + Please mind: the offset include FCB block (even the FCB block is not exported) + The offset is 0x2000 for XIP images and 0x1000 for non-XIP images + """ + return self.get_app_offset(self.ivt_offset) + + @staticmethod + def get_app_offset(ivt_offset: int) -> int: + """:return: offset in the binary image, where the application starts. + + :param ivt_offset: Offset of IVT segment + Please mind: the offset include FCB block (even the FCB block is not exported) The offset is 0x2000 for XIP images and 0x1000 for non-XIP images """ return ( BootImgRT.XIP_APP_OFFSET - if (self.ivt_offset == self.IVT_OFFSET_NOR_FLASH) + if (ivt_offset == BootImgRT.IVT_OFFSET_NOR_FLASH) else BootImgRT.NON_XIP_APP_OFFSET ) @@ -1005,7 +1017,6 @@ def _find_ivt_pos( end_pos = min(start_pos + size, end_pos) for ivt_ofs in cls.IVT_OFFSETS: - if start_pos + ivt_ofs > end_pos: break strm.seek(start_pos + ivt_ofs) @@ -1014,7 +1025,7 @@ def _find_ivt_pos( header = Header.parse(header_data, required_tag=SegTag.IVT2) if (header.length == SegIVT2.SIZE) and (header.param in cls.VERSIONS): return header, start_pos + ivt_ofs, end_pos - except UnparsedException: # ignore different header tags + except SPSDKParsingError: # ignore different header tags pass raise SPSDKError("IVT not found") @@ -1083,7 +1094,7 @@ def parse( xmcd_header = XMCDHeader.parse(read_raw_data(stream, XMCDHeader.SIZE)) xmcd_data = read_raw_data(stream, xmcd_header.config_data_size) obj.xmcd = SegXMCD(header=xmcd_header, config_data=xmcd_data) - except UnparsedException: + except SPSDKParsingError: # No XMCD found pass # Parse BDT @@ -1308,7 +1319,7 @@ def add_image( if address != 0: self.address = address else: - raise Exception("Unknown data type !") + raise SPSDKError("Unknown data type !") def export(self) -> bytes: """Export image as bytes array. @@ -1594,7 +1605,7 @@ def add_image( if address != 0: self.address = address else: - raise Exception("Unknown data type !") + raise SPSDKError("Unknown data type !") def export(self) -> bytes: """Export Image as bytes array. @@ -1955,7 +1966,7 @@ def add_image( elif img_type == EnumAppType.SCD: if self._sdc_address == 0: - raise Exception("SCFW have to be define before SCD!") + raise SPSDKError("SCFW have to be define before SCD!") image_index = self.bdt[0].images_count self.bdt[0].images[image_index].image_destination = self._sdc_address self.bdt[0].images[image_index].image_entry = 0 @@ -1969,7 +1980,7 @@ def add_image( self.app[0][image_index].padding = self._compute_padding(len(data), self.SECTOR_SIZE) else: - raise Exception("Unknown data type!") + raise SPSDKError("Unknown data type!") def export(self) -> bytes: """Export Image as binary blob.""" @@ -2363,7 +2374,7 @@ def add_image( elif img_type == EnumAppType.SCD: if self._scd_address == 0: - raise Exception("SCFW have to be define before SCD!") + raise SPSDKError("SCFW have to be define before SCD!") self.scd.data = data self.scd.padding = self._compute_padding(len(data), self.SECTOR_SIZE) self.bdt[0].scd.image_destination = self._scd_address @@ -2372,7 +2383,7 @@ def add_image( self.ivt[0].scd_address = self.bdt[0].scd.image_destination else: - raise Exception("Unknown image type!") + raise SPSDKError("Unknown image type!") def export(self) -> bytes: """Export.""" diff --git a/spsdk/image/mbi_mixin.py b/spsdk/image/mbi_mixin.py index 4e2d0fcd..52ba7a9b 100644 --- a/spsdk/image/mbi_mixin.py +++ b/spsdk/image/mbi_mixin.py @@ -8,14 +8,15 @@ """Master Boot Image.""" +import logging import struct from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union from crcmod.predefined import mkPredefinedCrcFun from spsdk import SPSDKError -from spsdk.crypto import SignatureProvider -from spsdk.image import IMG_DATA_FOLDER +from spsdk.crypto.signature_provider import SignatureProvider, get_signature_provider +from spsdk.exceptions import SPSDKUnsupportedOperation from spsdk.image.keystore import KeySourceType, KeyStore from spsdk.image.trustzone import TrustZone, TrustZoneType from spsdk.utils.crypto import crypto_backend @@ -25,7 +26,7 @@ from spsdk.utils.images import BinaryImage from spsdk.utils.misc import align_block, find_file, load_binary, load_configuration, value_to_int -SCHEMA_FILE = IMG_DATA_FOLDER + "/sch_mbimg.yml" +logger = logging.getLogger(__name__) class MasterBootImageManifest: @@ -225,6 +226,7 @@ def export(self, start_addr: int) -> bytes: # Mbi Mixins # **************************************************************************************************** + # pylint: disable=invalid-name class Mbi_Mixin: """Base class for Master BOtt Image Mixin classes.""" @@ -657,10 +659,10 @@ class Mbi_MixinCertBlockV2(Mbi_Mixin): """Master Boot Image certification block V2 class.""" VALIDATION_SCHEMAS: List[str] = ["cert_prv_key"] - NEEDED_MEMBERS: List[str] = ["cert_block", "priv_key_data"] + NEEDED_MEMBERS: List[str] = ["cert_block", "signature_provider"] cert_block: Optional[CertBlockV2] - priv_key_data: Optional[bytes] + signature_provider: Optional[SignatureProvider] search_paths: Optional[List[str]] def mix_len(self) -> int: @@ -684,8 +686,10 @@ def mix_load_from_config(self, config: Dict[str, Any]) -> None: :param config: Dictionary with configuration fields. """ self.cert_block = CertBlockV2.from_config(config, self.search_paths) - self.priv_key_data = load_binary( - config["mainCertPrivateKeyFile"], search_paths=self.search_paths + self.signature_provider = get_signature_provider( + sp_cfg=config.get("signProvider"), + local_file_key=config.get("mainCertPrivateKeyFile"), + search_paths=self.search_paths, ) def mix_validate(self) -> None: @@ -695,23 +699,26 @@ def mix_validate(self) -> None: """ if not self.cert_block: raise SPSDKError("Certification block is missing") - - if not self.priv_key_data: - raise SPSDKError("Certification block Private key is missing") - - if not self.cert_block.verify_private_key(self.priv_key_data): # type: ignore - raise SPSDKError( - "Signature verification failed, private key does not match to certificate" - ) + if not self.signature_provider: + raise SPSDKError("Signature provider is not defined") + public_key = self.cert_block.certificates[-1].public_key + try: + result = self.signature_provider.verify_public_key(public_key.dump()) + if not result: + raise SPSDKError( + "Signature verification failed, public key does not match to private key" + ) + logger.debug("The verification of private key pair integrity has been successful.") + except SPSDKUnsupportedOperation: + logger.warning("Signature provider could not verify the integrity of private key pair.") class Mbi_MixinCertBlockV31(Mbi_Mixin): """Master Boot Image certification block V3.1 class.""" VALIDATION_SCHEMAS: List[str] = [ - "use_isk", - "signing_cert_prv_key", "signing_root_prv_key", + "signature_provider", "signing_prv_key_lpc55s3x", ] NEEDED_MEMBERS: List[str] = ["cert_block", "signature_provider"] @@ -742,17 +749,16 @@ def mix_load_from_config(self, config: Dict[str, Any]) -> None: :param config: Dictionary with configuration fields. """ self.cert_block = CertBlockV31.from_config(config, search_paths=self.search_paths) - # if ISK is used, we use for signing the ISK certificate instead of root - if self.cert_block.isk_certificate: - signing_private_key_path = config.get("signingCertificatePrivateKeyFile") - else: - signing_private_key_path = config.get("mainRootCertPrivateKeyFile") - assert signing_private_key_path - signing_private_key_path = find_file( - signing_private_key_path, search_paths=self.search_paths + + private_key_file_name = ( + config.get("signingCertificatePrivateKeyFile") + if self.cert_block and self.cert_block.isk_certificate + else config.get("mainRootCertPrivateKeyFile") ) - self.signature_provider = SignatureProvider.create( - f"type=file;file_path={signing_private_key_path}" + self.signature_provider = get_signature_provider( + sp_cfg=config.get("signProvider"), + local_file_key=private_key_file_name, + search_paths=self.search_paths, ) def mix_validate(self) -> None: @@ -953,11 +959,12 @@ def store_ctr_init_vector(self, ctr_iv: Optional[bytes] = None) -> None: class Mbi_MixinSignDigest(Mbi_Mixin): """Master Boot Image Signature Digest.""" - VALIDATION_SCHEMAS: List[str] = ["attach_sign_digest", "use_isk", "elliptic_curves"] + VALIDATION_SCHEMAS: List[str] = ["attach_sign_digest"] NEEDED_MEMBERS: List[str] = ["attach_sign_digest"] SIGN_DIGEST_VALUES: Dict[str, int] = {"sha256": 32, "sha384": 48} attach_sign_digest: Optional[str] + cert_block: Optional[CertBlockV31] signature_provider: Optional[SignatureProvider] def mix_len(self) -> int: @@ -973,14 +980,7 @@ def mix_load_from_config(self, config: Dict[str, Any]) -> None: :param config: Dictionary with configuration fields. """ attach_sign_digest = config.get("attachSignDigest", False) - use_isk = config.get("useIsk", False) - if attach_sign_digest: - cfg_root_curve = config.get( - "iskCertificateEllipticCurve" if use_isk else "rootCertificateEllipticCurve" - ) - self.attach_sign_digest = "sha256" if cfg_root_curve == "secp256r1" else "sha384" - else: - self.attach_sign_digest = None + self.attach_sign_digest = self.get_sign_digest() if attach_sign_digest else None def mix_validate(self) -> None: """Validate the setting of image. @@ -995,12 +995,12 @@ def mix_validate(self) -> None: def get_sign_digest(self) -> Optional[str]: """Get sign digest type from signature provider. + :raises SPSDKError: Missing defined signature provider in class. :return: Type of signature digest. """ - if self.signature_provider: - return "sha256" if self.signature_provider.signature_length == 32 else "sha384" - - return None + if not self.signature_provider: + raise SPSDKError("MBI: Signature Digest needs to has defined signature provider.") + return "sha256" if self.signature_provider.signature_length // 2 == 32 else "sha384" class Mbi_MixinNXPImage(Mbi_Mixin): @@ -1131,6 +1131,7 @@ def collect_data(self) -> bytes: assert self.app and self.tz and self.cert_block self.cert_block.alignment = 4 self.cert_block.image_length = self.app_len + logger.info(f"RKTH: {self.cert_block.rkht.hex()}") app = self.get_app_data() if hasattr(self, "get_app_data") else self.app return self.update_ivt( app + self.cert_block.export() + self.tz.export(), @@ -1193,7 +1194,7 @@ def sign(self, image: bytes) -> bytes: class Mbi_ExportMixinRsaSign(Mbi_ExportMixin): """Export Mixin to handle sign by RSA.""" - priv_key_data: Optional[bytes] + signature_provider: Optional[SignatureProvider] def sign(self, image: bytes) -> bytes: """Do calculation of RSA signature and return updated image with it. @@ -1201,8 +1202,9 @@ def sign(self, image: bytes) -> bytes: :param image: Input raw image. :return: Image enriched by RSA signature at end of image. """ - assert self.priv_key_data - return image + crypto_backend().rsa_sign(self.priv_key_data, image) + assert self.signature_provider + signature = self.signature_provider.sign(image) + return image + signature class Mbi_ExportMixinEccSign(Mbi_ExportMixin): diff --git a/spsdk/image/mbimg.py b/spsdk/image/mbimg.py index d959e92e..56ff87f0 100644 --- a/spsdk/image/mbimg.py +++ b/spsdk/image/mbimg.py @@ -9,14 +9,14 @@ """Master Boot Image.""" +import os from copy import deepcopy from inspect import isclass from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union from Crypto.Cipher import AES -from ruamel.yaml import YAML -from spsdk.crypto import SignatureProvider +from spsdk.crypto.signature_provider import SignatureProvider from spsdk.exceptions import SPSDKValueError from spsdk.image import IMG_DATA_FOLDER, MBIMG_SCH_FILE from spsdk.image.exceptions import SPSDKUnsupportedImageType @@ -54,7 +54,7 @@ ) from spsdk.image.trustzone import TrustZone from spsdk.utils.crypto.cert_blocks import CertBlockV2, CertBlockV31 -from spsdk.utils.misc import align_block, get_key_by_val +from spsdk.utils.misc import align_block, get_key_by_val, load_configuration from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas, check_config PLAIN_IMAGE = (0x00, "Plain Image (either XIP or Load-to-RAM)") @@ -64,7 +64,8 @@ SIGNED_XIP_IMAGE = (0x04, "Plain Signed XIP Image") CRC_XIP_IMAGE = (0x05, "Plain CRC XIP Image") -DEVICE_FILE = IMG_DATA_FOLDER + "/database.yml" +DEVICE_FILE = os.path.join(IMG_DATA_FOLDER, "database.yaml") + # pylint: disable=too-many-ancestors def get_mbi_class(config: Dict[str, Any]) -> Type["MasterBootImage"]: @@ -74,8 +75,7 @@ def get_mbi_class(config: Dict[str, Any]) -> Type["MasterBootImage"]: :return: MBI Class. """ schema_cfg = ValidationSchemas.get_schema_file(MBIMG_SCH_FILE) - with open(DEVICE_FILE) as f: - device_cfg = YAML(typ="safe").load(f) + device_cfg = load_configuration(DEVICE_FILE) # Validate needed configuration to recognize MBI class check_config(config, [schema_cfg["image_type"], schema_cfg["family"]]) try: @@ -104,8 +104,7 @@ def get_mbi_classes(family: str) -> Dict[str, Tuple[Type["MasterBootImage"], str :return: Dictionary with key like image name and values are Tuple with it's MBI Class and target and authentication type. """ - with open(DEVICE_FILE) as f: - device_cfg = YAML(typ="safe").load(f) + device_cfg = load_configuration(DEVICE_FILE) if not family in device_cfg["devices"]: raise SPSDKValueError("Not supported family for Master Boot Image") @@ -179,8 +178,7 @@ def mbi_get_supported_families() -> List[str]: :return: List of supported family names. """ - with open(DEVICE_FILE) as f: - device_cfg = YAML(typ="safe").load(f) + device_cfg = load_configuration(DEVICE_FILE) devices: Dict[str, Any] = device_cfg["devices"] return list(devices.keys()) @@ -278,9 +276,7 @@ def get_supported_families(cls) -> List[str]: :return: List of supported families. """ ret = set() - with open(DEVICE_FILE) as f: - device_cfg = YAML(typ="safe").load(f) - + device_cfg = load_configuration(DEVICE_FILE) devices: Dict[str, Dict] = device_cfg["devices"] for device, dev_val in devices.items(): images: Dict[str, Dict[str, str]] = dev_val["images"] @@ -321,6 +317,7 @@ def validate(self) -> None: # Master Boot Image Class (LPC55) ######################################################################################################################## + # pylint: disable=invalid-name # pylint: disable=abstract-method class Mbi_PlainXip( @@ -411,19 +408,19 @@ def __init__( app: Optional[bytes] = None, trust_zone: Optional[TrustZone] = None, cert_block: Optional[CertBlockV2] = None, - priv_key_data: Optional[bytes] = None, + signature_provider: Optional[SignatureProvider] = None, ) -> None: """Constructor for Master Boot Signed XiP Image for LPC55xxx family. :param app: Application image data, defaults to None :param trust_zone: TrustZone object, defaults to None :param cert_block: Certification block of image, defaults to None - :param priv_key_data: Private key used to sign image, defaults to None + :param signature_provider: Signature provider to sign final image, defaults to None """ self.app = align_block(app) if app else None self.tz = trust_zone or TrustZone.enabled() self.cert_block = cert_block - self.priv_key_data = priv_key_data + self.signature_provider = signature_provider super().__init__() @@ -447,7 +444,7 @@ def __init__( trust_zone: Optional[TrustZone] = None, load_addr: Optional[int] = None, cert_block: Optional[CertBlockV2] = None, - priv_key_data: Optional[bytes] = None, + signature_provider: Optional[SignatureProvider] = None, ) -> None: """Constructor for Master Boot Signed XiP Image for LPC55xxx family. @@ -455,13 +452,13 @@ def __init__( :param trust_zone: TrustZone object, defaults to None :param load_addr: Load/Execution address in RAM of image, defaults to 0 :param cert_block: Certification block of image, defaults to None - :param priv_key_data: Private key used to sign image, defaults to None + :param signature_provider: Signature provider to sign final image, defaults to None """ self.app = align_block(app) if app else None self.tz = trust_zone or TrustZone.enabled() self.load_address = load_addr self.cert_block = cert_block - self.priv_key_data = priv_key_data + self.signature_provider = signature_provider super().__init__() @@ -528,7 +525,7 @@ def __init__( trust_zone: Optional[TrustZone] = None, load_addr: Optional[int] = None, cert_block: Optional[CertBlockV2] = None, - priv_key_data: Optional[bytes] = None, + signature_provider: Optional[SignatureProvider] = None, hmac_key: Optional[Union[bytes, str]] = None, key_store: Optional[KeyStore] = None, hwk: bool = False, @@ -540,7 +537,7 @@ def __init__( :param trust_zone: TrustZone object, defaults to None :param load_addr: Load/Execution address in RAM of image, defaults to 0 :param cert_block: Certification block of image, defaults to None - :param priv_key_data: Private key used to sign image, defaults to None + :param signature_provider: Signature provider to sign final image, defaults to None :param hmac_key: HMAC key of image, defaults to None :param key_store: Optional KeyStore object for image, defaults to None :param hwk: Enable HW user mode keys, defaults to false @@ -550,7 +547,7 @@ def __init__( self.load_address = load_addr self.tz = trust_zone or TrustZone.enabled() self.cert_block = cert_block - self.priv_key_data = priv_key_data + self.signature_provider = signature_provider self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key self.key_store = key_store self.user_hw_key_enabled = hwk @@ -631,7 +628,7 @@ def __init__( trust_zone: Optional[TrustZone] = None, load_addr: Optional[int] = None, cert_block: Optional[CertBlockV2] = None, - priv_key_data: Optional[bytes] = None, + signature_provider: Optional[SignatureProvider] = None, hmac_key: Optional[Union[bytes, str]] = None, key_store: Optional[KeyStore] = None, ctr_init_vector: Optional[bytes] = None, @@ -644,7 +641,7 @@ def __init__( :param trust_zone: TrustZone object, defaults to None :param load_addr: Load/Execution address in RAM of image, defaults to 0 :param cert_block: Certification block of image, defaults to None - :param priv_key_data: Private key used to sign image, defaults to None + :param signature_provider: Signature provider to sign final image, defaults to None :param hwk: Enable HW user mode keys, defaults to false :param key_store: Optional KeyStore object for image, defaults to None :param hmac_key: HMAC key of image, defaults to None @@ -655,7 +652,7 @@ def __init__( self.app_table = app_table self.tz = trust_zone or TrustZone.enabled() self.cert_block = cert_block - self.priv_key_data = priv_key_data + self.signature_provider = signature_provider self.user_hw_key_enabled = hwk self.key_store = key_store self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key @@ -768,7 +765,7 @@ def __init__( trust_zone: Optional[TrustZone] = None, load_addr: Optional[int] = None, cert_block: Optional[CertBlockV2] = None, - priv_key_data: Optional[bytes] = None, + signature_provider: Optional[SignatureProvider] = None, hwk: bool = False, ) -> None: """Constructor for Master Boot Plain Signed XiP Image for RTxxx family. @@ -777,14 +774,14 @@ def __init__( :param trust_zone: TrustZone object, defaults to None :param load_addr: Load/Execution address in RAM of image, defaults to 0 :param cert_block: Certification block of image, defaults to None - :param priv_key_data: Private key used to sign image, defaults to None + :param signature_provider: Signature provider to sign final image, defaults to None :param hwk: Enable HW user mode keys, defaults to false """ self.app = align_block(app) if app else None self.tz = trust_zone or TrustZone.enabled() self.load_address = load_addr self.cert_block = cert_block - self.priv_key_data = priv_key_data + self.signature_provider = signature_provider self.user_hw_key_enabled = hwk super().__init__() diff --git a/spsdk/image/segments.py b/spsdk/image/segments.py index 9b9c5270..23a00157 100644 --- a/spsdk/image/segments.py +++ b/spsdk/image/segments.py @@ -14,7 +14,12 @@ from typing import Dict, Iterator, List, Optional, Sequence, Tuple, Union from spsdk import SPSDKError -from spsdk.exceptions import SPSDKValueError +from spsdk.exceptions import ( + SPSDKCorruptedException, + SPSDKParsingError, + SPSDKSyntaxError, + SPSDKValueError, +) from spsdk.utils.misc import DebugInfo, align, align_block, extend_block, size_fmt from .bee import BEE_ENCR_BLOCK_SIZE, BeeRegionHeader @@ -31,7 +36,7 @@ EnumWriteOps, parse_command, ) -from .header import CorruptedException, Header, Header2, SegTag, UnparsedException +from .header import Header, Header2, SegTag from .secret import MAC, BaseClass logger = logging.getLogger(__name__) @@ -1011,7 +1016,7 @@ def export_txt(self, txt_data: Optional[str] = None) -> str: for cmd in self._commands: if isinstance(cmd, CmdWriteData): - for (address, value) in cmd: + for address, value in cmd: txt_data += ( f"{write_ops[cmd.ops]} {cmd.num_bytes} 0x{address:08X} 0x{value:08X}\n" ) @@ -1070,7 +1075,7 @@ def parse(cls, data: bytes) -> "SegDCD": """Parse segment from bytes array. :param data: The bytes array of DCD segment - :raises CorruptedException: Exception caused by corrupted data + :raises SPSDKCorruptedException: Exception caused by corrupted data :return: SegDCD object """ header = Header.parse(data, 0, SegTag.DCD) @@ -1080,7 +1085,7 @@ def parse(cls, data: bytes) -> "SegDCD": try: cmd_obj = parse_command(data, index) except ValueError as exc: - raise CorruptedException("Unknown command at position: " + hex(index)) from exc + raise SPSDKCorruptedException("Unknown command at position: " + hex(index)) from exc obj.append(cmd_obj) index += cmd_obj.size @@ -1102,7 +1107,7 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: :param dcd_obj: result of the builder :param cmd: command with arguments - :raises SyntaxError: command is corrupted + :raises SPSDKError: command is corrupted :raises SPSDKError: When command is unsupported """ # ---------------------------- @@ -1123,7 +1128,7 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: self.cmd_write = None if cmd[1] not in EnumEngine: - raise SyntaxError( + raise SPSDKError( f"Unlock CMD: wrong engine parameter at line {self.line_cnt - 1}" ) @@ -1136,7 +1141,7 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: elif cmd_tuple[0] == "write": if len(cmd) < 4: - raise SyntaxError(f"Write CMD: not enough arguments at line {self.line_cnt - 1}") + raise SPSDKError(f"Write CMD: not enough arguments at line {self.line_cnt - 1}") ops = cmd_tuple[1] numbytes = int(cmd[1]) @@ -1155,7 +1160,9 @@ def _parse_cmd(self, dcd_obj: SegDCD, cmd: List[str]) -> None: else: if len(cmd) < 4: - raise SyntaxError(f"Check CMD: not enough arguments at line {self.line_cnt - 1}") + raise SPSDKSyntaxError( + f"Check CMD: not enough arguments at line {self.line_cnt - 1}" + ) if self.cmd_write is not None: dcd_obj.append(self.cmd_write) @@ -1173,7 +1180,6 @@ def build(self, text: str) -> SegDCD: :param text: input text to import :return: SegDCD object - :raise SyntaxError: if input format is not valid """ dcd_obj = SegDCD(enabled=True) cmd_mline = False @@ -1435,8 +1441,8 @@ def parse(cls, data: bytes, offset: int = 0) -> "SegCSF": :param data: The bytes array of CSF segment :param offset: to start parsing the data - :raises CorruptedException: When there is unknown command - :raises CorruptedException: When command can not be parsed + :raises SPSDKCorruptedException: When there is unknown command + :raises SPSDKCorruptedException: When command can not be parsed :return: SegCSF instance """ header = Header.parse(data, offset, SegTag.CSF) @@ -1448,7 +1454,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "SegCSF": cmd_obj = parse_command(data, offset + index) obj.append_command(cmd_obj) except ValueError as exc: - raise CorruptedException( + raise SPSDKCorruptedException( "Failed to parse command at position: " + hex(offset + index) ) from exc index += cmd_obj.size @@ -1490,7 +1496,7 @@ def export(self) -> bytes: return pack( self.FORMAT, self.block_size & 0xFF, - self.block_type << 4 + self.block_size >> 8, + (self.block_type << 4) + (self.block_size >> 8), self.interface << 4 + self.instance, self.tag << 4 + self.version, ) @@ -1515,14 +1521,14 @@ def parse(cls, data: bytes) -> "XMCDHeader": size_low, type_size, interface_instance, tag_ver = unpack_from(cls.FORMAT, data) tag = (tag_ver & 0xF0) >> 4 if tag != cls.TAG: - raise UnparsedException(f"Invalid TAG for XMCDHeader {tag}. Expected: {cls.TAG}") + raise SPSDKParsingError(f"Invalid TAG for XMCDHeader {tag}. Expected: {cls.TAG}") version = tag_ver & 0x0F if version != 0: - raise UnparsedException(f"Invalid version {version}. Expected: 0") + raise SPSDKParsingError(f"Invalid version {version}. Expected: 0") interface = (interface_instance & 0xF0) >> 4 instance = interface_instance & 0x0F block_type = (type_size & 0xF0) >> 4 - block_size = type_size & 0x0F + block_size = (type_size & 0x0F) << 8 block_size += size_low return XMCDHeader( interface=interface, instance=instance, block_type=block_type, block_size=block_size diff --git a/spsdk/image/segments_base.py b/spsdk/image/segments_base.py index aace6d49..675b0c5a 100644 --- a/spsdk/image/segments_base.py +++ b/spsdk/image/segments_base.py @@ -1,10 +1,12 @@ -"""This module contains generic implementation of image segment.""" #!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause + +"""This module contains generic implementation of image segment.""" + import abc from typing import Any, Dict diff --git a/spsdk/image/trustzone.py b/spsdk/image/trustzone.py index 3f9443ac..78f26117 100644 --- a/spsdk/image/trustzone.py +++ b/spsdk/image/trustzone.py @@ -138,6 +138,7 @@ def get_validation_schemas_family(cls) -> List[Dict[str, Any]]: :return: List of validation schemas for TZ supported families. """ sch_cfg = ValidationSchemas.get_schema_file(TZ_SCH_FILE) + sch_cfg["tz_family_rev"]["properties"]["family"]["enum"] = cls.get_supported_families() return [sch_cfg["tz_family_rev"]] @classmethod @@ -170,7 +171,7 @@ def get_validation_schemas(cls, family: str, revision: str = "latest") -> List[D if "patternProperties" in sch_cfg["tz"]["properties"]["trustZonePreset"].keys(): sch_cfg["tz"]["properties"]["trustZonePreset"].pop("patternProperties") sch_cfg["tz"]["properties"]["trustZonePreset"]["properties"] = preset_properties - + sch_cfg["tz_family_rev"]["properties"]["family"]["enum"] = cls.get_supported_families() return [sch_cfg["tz_family_rev"], sch_cfg["tz"]] except (KeyError, SPSDKError) as exc: raise SPSDKError(f"Family {family} or revision {revision} is not supported") from exc diff --git a/spsdk/image/xmcd/__init__.py b/spsdk/image/xmcd/__init__.py index cae53e07..877c0e17 100644 --- a/spsdk/image/xmcd/__init__.py +++ b/spsdk/image/xmcd/__init__.py @@ -11,5 +11,5 @@ from spsdk import SPSDK_DATA_FOLDER XMCD_DATA_FOLDER: str = os.path.join(SPSDK_DATA_FOLDER, "image", "xmcd") -XMCD_SCH_FILE: str = os.path.join(XMCD_DATA_FOLDER, "sch_xmcd.yml") -XMCD_DATABASE_FILE: str = os.path.join(XMCD_DATA_FOLDER, "database.yml") +XMCD_SCH_FILE: str = os.path.join(XMCD_DATA_FOLDER, "sch_xmcd.yaml") +XMCD_DATABASE_FILE: str = os.path.join(XMCD_DATA_FOLDER, "database.yaml") diff --git a/spsdk/image/xmcd/xmcd.py b/spsdk/image/xmcd/xmcd.py index 8f4f7754..a44d0f64 100644 --- a/spsdk/image/xmcd/xmcd.py +++ b/spsdk/image/xmcd/xmcd.py @@ -82,29 +82,81 @@ def __init__(self, family: str, revision: str = "latest") -> None: :raises SPSDKValueError: Unsupported family. """ super().__init__(family, revision) - self.mem_type: Optional[str] = None - self.config_type_type: Optional[str] = None + self._mem_type: Optional[str] = None + self._config_type: Optional[str] = None self._registers: Registers = Registers(family, base_endianness="little") - @staticmethod - def get_database() -> Database: - """Get the devices database.""" - return Database(XMCD_DATABASE_FILE) + @property + def mem_type(self) -> str: + """Memory type.""" + return self._mem_type or "Unknown" + + @mem_type.setter + def mem_type(self, value: str) -> None: + """Memory type setter. + + :param value: Value to be set. + :raises SPSDKValueError: If givem memory type is not supported + """ + mem_types = XMCD.get_supported_memory_types(self.family, self.revision) + if value not in mem_types: + raise SPSDKValueError(f"Unsupported memory type:{value} not in {mem_types}") + self._mem_type = value + + @property + def config_type(self) -> str: + """Configuration type. It can be either simplified or full.""" + return self._config_type or "Unknown" + + @config_type.setter + def config_type(self, value: str) -> None: + """Configuration type setter. + + :param value: Value to be set. + :raises SPSDKValueError: If givem configuration type is not supported + """ + supported_config_types = XMCD.get_supported_configuration_types( + self.family, self.mem_type, self.revision + ) + if value not in supported_config_types: + raise SPSDKValueError( + f"Unsupported config type:{value} not in {supported_config_types}" + ) + self._config_type = value @property def registers(self) -> Registers: """Registers of segment.""" return self._registers + @registers.setter + def registers(self, value: Registers) -> None: + self._registers = value + + @staticmethod + def get_database() -> Database: + """Get the devices database.""" + return Database(XMCD_DATABASE_FILE) + def parse(self, binary: bytes) -> None: """Parse binary block into XMCD object. :param binary: binary image. + :raises SPSDKError: If given binary block size is not equal to block size in header """ header = XMCDHeader.parse(binary[: XMCDHeader.SIZE]) - mem_type = MemoryType(header.interface).name.lower() - config_type = ConfigurationBlockType(header.block_type).name.lower() - self._load_configuration(mem_type, config_type) + self.mem_type = MemoryType(header.interface).name.lower() + self.config_type = ConfigurationBlockType(header.block_type).name.lower() + option_size = None + if header.block_size != len(binary): + raise SPSDKError("Invalid XMCD configuration size") + if self.mem_type == "flexspi_ram" and self.config_type == "simplified": + if header.block_size - XMCDHeader.SIZE not in [4, 8]: + raise SPSDKError("Invalid XMCD configuration size") + option_size = 0 if len(binary[XMCDHeader.SIZE :]) == 4 else 1 + self.registers = self.load_registers( + self.family, self.mem_type, self.config_type, self.revision, option_size=option_size + ) super().parse(binary) crc = self.calculate_crc(binary) logger.info(f"CRC value: {crc!r}") @@ -118,10 +170,18 @@ def load_from_config(config: Dict) -> "XMCD": """ family = config["family"] revision = config.get("revision", "latest") - xmcd_settings = config["xmcd_settings"] + xmcd_settings: Dict[str, Dict] = config["xmcd_settings"] xmcd = XMCD(family=family, revision=revision) - xmcd._load_configuration(config["mem_type"], config["config_type"]) + xmcd.mem_type = config["mem_type"] + xmcd.config_type = config["config_type"] + + option_size = None + if xmcd.mem_type == "flexspi_ram" and xmcd.config_type == "simplified": + option_size = xmcd_settings["configOption0"]["bitfields"]["optionSize"] + xmcd._registers = xmcd.load_registers( + xmcd.family, xmcd.mem_type, xmcd.config_type, xmcd.revision, option_size=option_size + ) xmcd.registers.load_yml_config(xmcd_settings) return xmcd @@ -139,7 +199,7 @@ def calculate_crc(self, data: bytes) -> bytes: def create_config(self) -> str: """Create current configuration YAML. - :raises AttributeError: Registers are not loaded in the object. + :raises SPSDKError: Registers are not loaded in the object. :return: Configuration of XMCD Block. """ config = CM() @@ -148,7 +208,7 @@ def create_config(self) -> str: config["mem_type"] = self.mem_type or "Unknown" config["config_type"] = self.config_type or "Unknown" if len(self.registers.get_registers()) == 0: - raise AttributeError( + raise SPSDKError( "Registers are not loaded. Load the configuration or parse binary first." ) config["xmcd_settings"] = self.registers.create_yml_config() @@ -161,7 +221,11 @@ def create_config(self) -> str: @classmethod def get_validation_schemas( - cls, family: str, mem_type: str, config_type: str, revision: str = "latest" + cls, + family: str, + mem_type: str, + config_type: str, + revision: str = "latest", ) -> List[Dict[str, Any]]: """Create the validation schema. @@ -240,21 +304,6 @@ def get_supported_configuration_types( mem_types = cls.get_memory_types(family, revision) return list(mem_types[mem_type].keys()) - def _load_configuration(self, mem_type: str, config_type: str) -> None: - mem_types = XMCD.get_supported_memory_types(self.family, self.revision) - if mem_type not in mem_types: - raise SPSDKValueError(f"Unsupported memory type:{mem_type} not in {mem_types}") - self.mem_type = mem_type - supported_config_types = XMCD.get_supported_configuration_types( - self.family, mem_type, self.revision - ) - if config_type not in supported_config_types: - raise SPSDKValueError( - f"Unsupported config type:{config_type} not in {supported_config_types}" - ) - self.config_type = config_type - self._registers = self.load_registers(self.family, mem_type, config_type, self.revision) - @staticmethod def _load_header_registers( family: str, mem_type: str, config_type: str, revision: str @@ -281,7 +330,11 @@ def _load_header_registers( @staticmethod def _load_block_registers( - family: str, mem_type: str, config_type: str, revision: str + family: str, + mem_type: str, + config_type: str, + revision: str, + filter_reg: Optional[List[str]] = None, ) -> Registers: """Load block registers of segment. @@ -297,21 +350,37 @@ def _load_block_registers( raise SPSDKValueError(f"Unsupported combination: {mem_type}:{config_type}") block_file_path = os.path.join(XMCD_DATA_FOLDER, block_config) regs = Registers(family, base_endianness="little") - - regs.load_registers_from_xml(block_file_path) + regs.load_registers_from_xml(block_file_path, filter_reg=filter_reg) return regs @staticmethod - def load_registers(family: str, mem_type: str, config_type: str, revision: str) -> Registers: + def load_registers( + family: str, + mem_type: str, + config_type: str, + revision: str, + option_size: Optional[int] = None, + ) -> Registers: """Load all registers of segment. :param family: Family description. :param mem_type: Used memory type. :param config_type: Config type: either simplified or full. :param revision: Chip revision specification, as default, latest is used. + :param option_size: Number of configuration words to be loaded. + + :raises SPSDKValueError: If option_size has invalid value(only 0 or 1 are allowed) """ regs = XMCD._load_header_registers(family, mem_type, config_type, revision) - block_regs = XMCD._load_block_registers(family, mem_type, config_type, revision) + filter_reg = [] + if mem_type == "flexspi_ram" and config_type == "simplified": + if option_size is not None and option_size > 1: + raise SPSDKValueError("Option size can be either 0 or 1") + if option_size == 0: + filter_reg = ["configOption1"] + block_regs = XMCD._load_block_registers( + family, mem_type, config_type, revision, filter_reg=filter_reg + ) for block_reg in block_regs.get_registers(): block_reg.offset += XMCDHeader.SIZE * 8 regs.add_register(block_reg) diff --git a/spsdk/mboot/commands.py b/spsdk/mboot/commands.py index b17fa428..cdbe1e30 100644 --- a/spsdk/mboot/commands.py +++ b/spsdk/mboot/commands.py @@ -49,6 +49,7 @@ class CommandTag(Enum): TRUST_PROVISIONING = (0x16, "TrustProvisioning", "Trust Provisioning") FUSE_READ = (0x17, "ReadFuse", "Read Fuse") UPDATE_LIFE_CYCLE = (0x18, "UpdateLifeCycle", "Update Life Cycle") + ELE_MESSAGE = (0x19, "EleMessage", "Send EdgeLock Enclave Message") # reserved commands CONFIGURE_I2C = (0xC1, "ConfigureI2c", "Configure I2C") diff --git a/spsdk/mboot/error_codes.py b/spsdk/mboot/error_codes.py index b381f863..306d6e24 100644 --- a/spsdk/mboot/error_codes.py +++ b/spsdk/mboot/error_codes.py @@ -38,6 +38,7 @@ class StatusCode(Enum): FLASH_REGION_EXECUTE_ONLY = (108, "FlashRegionExecuteOnly", "FLASH Driver: Region Execute Only") FLASH_EXEC_IN_RAM_NOT_READY = (109, "FlashExecuteInRamFunctionNotReady", "FLASH Driver: Execute In RAM Function Not Ready") FLASH_COMMAND_NOT_SUPPORTED = (111, "FlashCommandNotSupported", "FLASH Driver: Command Not Supported") + FLASH_ECC_ERROR = (116, "FlashEccError", "FLASH Driver: ECC Error") FLASH_OUT_OF_DATE_CFPA_PAGE = (132, "FlashOutOfDateCfpaPage", "FLASH Driver: Out Of Date CFPA Page") FLASH_BLANK_IFR_PAGE_DATA = (133, "FlashBlankIfrPageData", "FLASH Driver: Blank IFR Page Data") FLASH_ENCRYPTED_REGIONS_ERASE_NOT_DONE_AT_ONCE = (134, "FlashEncryptedRegionsEraseNotDoneAtOnce", "FLASH Driver: Encrypted Regions Erase Not Done At Once") @@ -75,6 +76,9 @@ class StatusCode(Enum): OTFAD_INVALID_KEY = (502, "OtfadInvalidKey", "OTFAD Driver: Invalid Key") OTFAD_INVALID_KEY_BLOB = (503, "OtfadInvalidKeyBlob", "OTFAD Driver: Invalid Key Blob") + # Sending errors. + SENDING_OPERATION_CONDITION_ERROR = (1812, "SendOperationConditionError", "Send Operation Condition failed") + # SDMMC driver errors. # FlexSPI statuses. diff --git a/spsdk/mboot/interfaces/buspal_i2c.py b/spsdk/mboot/interfaces/buspal_i2c.py index a596592a..c65ad71a 100644 --- a/spsdk/mboot/interfaces/buspal_i2c.py +++ b/spsdk/mboot/interfaces/buspal_i2c.py @@ -12,6 +12,8 @@ from enum import Enum from typing import Any, Dict, List, Optional +from spsdk.exceptions import SPSDKError + from .base import Interface from .buspal import BBConstants, Buspal, BuspalMode @@ -120,7 +122,7 @@ def _send_frame( retry_cnt -= 1 self._send_frame(frames, wait_for_ack, retry_cnt) else: - raise error + raise SPSDKError("Failed retrying reading the I2C header frame") def _read_default(self, size: int) -> bytes: """Read 'length' amount of bytes from BUSPAL I2C device. diff --git a/spsdk/mboot/interfaces/buspal_spi.py b/spsdk/mboot/interfaces/buspal_spi.py index f54ba2db..357a6e85 100644 --- a/spsdk/mboot/interfaces/buspal_spi.py +++ b/spsdk/mboot/interfaces/buspal_spi.py @@ -13,6 +13,8 @@ from enum import Enum from typing import Any, Dict, List, Optional +from spsdk.exceptions import SPSDKError + from .base import Interface from .buspal import BBConstants, Buspal, BuspalMode @@ -167,7 +169,7 @@ def _send_frame( retry_cnt -= 1 self._send_frame(frames, wait_for_ack, retry_cnt) else: - raise error + raise SPSDKError("Failed retrying reading the SPI header frame") def _read_default(self, size: int) -> bytes: """Read 'length' amount of bytes from BUSPAL SPI device. diff --git a/spsdk/mboot/interfaces/uart.py b/spsdk/mboot/interfaces/uart.py index f66d8b37..32a57ced 100644 --- a/spsdk/mboot/interfaces/uart.py +++ b/spsdk/mboot/interfaces/uart.py @@ -21,6 +21,7 @@ from spsdk.mboot.commands import CmdPacket, CmdResponse, parse_cmd_response from spsdk.mboot.exceptions import McuBootConnectionError, McuBootDataAbortError from spsdk.utils.easy_enum import Enum +from spsdk.utils.exceptions import SPSDKTimeoutError from spsdk.utils.misc import Timeout from .base import MBootInterface @@ -120,6 +121,7 @@ def parse(cls, data: bytes) -> "PingResponse": MAX_PING_RESPONSE_DUMMY_BYTES = 50 MAX_UART_OPEN_ATTEMPTS = 3 + ######################################################################################################################## # UART Interface Class ######################################################################################################################## @@ -272,7 +274,7 @@ def _read(self, length: int) -> bytes: :param length: Number of bytes to read :return: Data read from the device - :raises TimeoutError: Time-out + :raises SPSDKTimeoutError: Time-out :raises McuBootConnectionError: When reading data from device fails """ try: @@ -280,7 +282,7 @@ def _read(self, length: int) -> bytes: except Exception as e: raise McuBootConnectionError(str(e)) from e if not data: - raise TimeoutError() + raise SPSDKTimeoutError() logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") return data @@ -288,7 +290,7 @@ def _write(self, data: bytes) -> None: """Send data to device. :param data: Data to send - :raises TimeoutError: Time-out + :raises SPSDKTimeoutError: Time-out :raises McuBootConnectionError: When sending the data fails """ logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]") @@ -298,7 +300,7 @@ def _write(self, data: bytes) -> None: self.device.write(data) self.device.flush() except SerialTimeoutException as e: - raise TimeoutError( + raise SPSDKTimeoutError( f"Write timeout error. The timeout is set to {self.device.write_timeout} s. Consider increasing it." ) from e except Exception as e: diff --git a/spsdk/mboot/interfaces/usb.py b/spsdk/mboot/interfaces/usb.py index 7baadf0e..c7aa5301 100644 --- a/spsdk/mboot/interfaces/usb.py +++ b/spsdk/mboot/interfaces/usb.py @@ -13,6 +13,7 @@ import libusbsio +from spsdk.utils.exceptions import SPSDKTimeoutError from spsdk.utils.misc import get_hash from spsdk.utils.usbfilter import NXPUSBDeviceFilter, USBDeviceFilter @@ -228,7 +229,7 @@ def read(self) -> Union[CmdResponse, bytes]: :raises McuBootConnectionError: Raises an error if device is not opened for reading :raises McuBootConnectionError: Raises if device is not available :raises McuBootConnectionError: Raises if reading fails - :raises TimeoutError: Time-out + :raises SPSDKTimeoutError: Time-out """ if not self.is_opened: raise McuBootConnectionError("Device is not opened for reading") @@ -240,7 +241,7 @@ def read(self) -> Union[CmdResponse, bytes]: raise McuBootConnectionError(str(e)) from e if not raw_data: logger.error(f"Cannot read from HID device, error={result}") - raise TimeoutError() + raise SPSDKTimeoutError() # NOTE: uncomment the following when using KBoot/Flashloader v2.1 and older # import platform # if platform.system() == "Linux": diff --git a/spsdk/mboot/interfaces/usbsio.py b/spsdk/mboot/interfaces/usbsio.py index 12c2d050..77b09ef7 100644 --- a/spsdk/mboot/interfaces/usbsio.py +++ b/spsdk/mboot/interfaces/usbsio.py @@ -16,6 +16,7 @@ from spsdk import SPSDKError, SPSDKValueError from spsdk.mboot.exceptions import McuBootConnectionError +from spsdk.utils.exceptions import SPSDKTimeoutError from spsdk.utils.misc import value_to_int from spsdk.utils.usbfilter import USBDeviceFilter @@ -323,7 +324,7 @@ def _read(self, length: int) -> bytes: except Exception as e: raise McuBootConnectionError(str(e)) from e if result < 0 or not data: - raise TimeoutError() + raise SPSDKTimeoutError() logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") return data @@ -332,7 +333,7 @@ def _write(self, data: bytes) -> None: :param data: Data to send :raises McuBootConnectionError: When sending the data fails - :raises TimeoutError: When data could not be written + :raises SPSDKTimeoutError: When data could not be written """ logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]") try: @@ -342,7 +343,7 @@ def _write(self, data: bytes) -> None: except Exception as e: raise McuBootConnectionError(str(e)) from e if result < 0: - raise TimeoutError() + raise SPSDKTimeoutError() class UsbSioI2C(UsbSio): @@ -416,16 +417,16 @@ def _read(self, length: int) -> bytes: :param length: Number of bytes to read :return: Data read from the device - :raises TimeoutError: Time-out + :raises SPSDKTimeoutError: Time-out :raises McuBootConnectionError: When reading data from device fails - :raises TimeoutError: When no data received + :raises SPSDKTimeoutError: When no data received """ try: (data, result) = self.port.DeviceRead(devAddr=self.i2c_address, rxSize=length) except Exception as e: raise McuBootConnectionError(str(e)) from e if result < 0 or not data: - raise TimeoutError() + raise SPSDKTimeoutError() logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") return data @@ -442,4 +443,4 @@ def _write(self, data: bytes) -> None: except Exception as e: raise McuBootConnectionError(str(e)) from e if result < 0: - raise TimeoutError() + raise SPSDKTimeoutError() diff --git a/spsdk/mboot/mcuboot.py b/spsdk/mboot/mcuboot.py index b40a4c9c..52a71362 100644 --- a/spsdk/mboot/mcuboot.py +++ b/spsdk/mboot/mcuboot.py @@ -407,7 +407,6 @@ def _get_ext_memories(self) -> List[ExtMemRegion]: ext_mem_ids = [ExtMemId.QUAD_SPI0] for mem_id in ext_mem_ids: - try: values = self.get_property(PropertyTag.EXTERNAL_MEMORY_ATTRIBUTES, mem_id) except McuBootCommandError: @@ -1131,6 +1130,36 @@ def update_life_cycle(self, life_cycle: int) -> bool: cmd_packet = CmdPacket(CommandTag.UPDATE_LIFE_CYCLE, CommandFlag.NONE, life_cycle) return self._process_cmd(cmd_packet).status == StatusCode.SUCCESS + def ele_message( + self, cmdMsgAddr: int, cmdMsgCnt: int, respMsgAddr: int, respMsgCnt: int + ) -> bool: + """Send EdgeLock Enclave message. + + :param cmdMsgAddr: Address in RAM where is prepared the command message words + :param cmdMsgCnt: Count of 32bits command words + :param respMsgAddr: Address in RAM where the command store the response + :param respMsgCnt: Count of 32bits response words + + :return: False in case of any problems, True otherwise. + """ + logger.info( + f"CMD: EleMessage Command (cmdMsgAddr=0x{cmdMsgAddr:08X}, cmdMsgCnt={cmdMsgCnt})" + ) + if respMsgCnt: + logger.info( + f"CMD: EleMessage Response (respMsgAddr=0x{respMsgAddr:08X}, respMsgCnt={respMsgCnt})" + ) + cmd_packet = CmdPacket( + CommandTag.ELE_MESSAGE, + CommandFlag.NONE, + 0, # reserved for future use as a sub command ID or anything else + cmdMsgAddr, + cmdMsgCnt, + respMsgAddr, + respMsgCnt, + ) + return self._process_cmd(cmd_packet).status == StatusCode.SUCCESS + def tp_hsm_gen_key( self, key_type: int, diff --git a/spsdk/pfr/pfrc.py b/spsdk/pfr/pfrc.py index c9777e2f..94d7d45c 100644 --- a/spsdk/pfr/pfrc.py +++ b/spsdk/pfr/pfrc.py @@ -14,7 +14,6 @@ from spsdk.pfr import PFR_DATA_FOLDER, Processor, Translator from spsdk.pfr.exceptions import SPSDKPfrcMissingConfigError, SPSDKPfrConfigError -from spsdk.pfr.pfr import PfrConfiguration from spsdk.utils.database import Database from spsdk.utils.misc import load_configuration diff --git a/spsdk/pfr/translator.py b/spsdk/pfr/translator.py index c4cce5e9..36678f13 100644 --- a/spsdk/pfr/translator.py +++ b/spsdk/pfr/translator.py @@ -11,7 +11,7 @@ from typing import Optional from .exceptions import SPSDKPfrcMissingConfigError -from .pfr import CFPA, CMPA, PfrConfiguration +from .pfr import CFPA, CMPA logger = logging.getLogger(__name__) diff --git a/spsdk/sbfile/sb1/images.py b/spsdk/sbfile/sb1/images.py index b4339ff4..fb2b2fd4 100644 --- a/spsdk/sbfile/sb1/images.py +++ b/spsdk/sbfile/sb1/images.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -212,7 +212,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1": :param data: given binary data to be converted :param offset: to start parsing the data :return: converted instance - :raise ValueError: raised when digest does not match + :raises SPSDKError: raised when digest does not match :raises SPSDKError: Raised when section is invalid """ obj = SecureBootV1() @@ -236,6 +236,6 @@ def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1": # authentication code sha1_auth = crypto_backend().hash(data[offset:cur_pos], "sha1") if sha1_auth != data[cur_pos : cur_pos + len(sha1_auth)]: - raise ValueError("Authentication failure: digest does not match") + raise SPSDKError("Authentication failure: digest does not match") # done return obj diff --git a/spsdk/sbfile/sb2/commands.py b/spsdk/sbfile/sb2/commands.py index d15c8f4c..e3b3bbfb 100644 --- a/spsdk/sbfile/sb2/commands.py +++ b/spsdk/sbfile/sb2/commands.py @@ -14,11 +14,11 @@ from crcmod.predefined import mkPredefinedCrcFun from spsdk import SPSDKError -from spsdk.mboot import ExtMemId, MemId +from spsdk.mboot import ExtMemId from spsdk.sbfile.misc import SecBootBlckSize from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.easy_enum import Enum -from spsdk.utils.misc import DebugInfo, swap16 +from spsdk.utils.misc import DebugInfo ######################################################################################################################## # Constants @@ -29,6 +29,7 @@ GROUP_ID_MASK = 0xF00 GROUP_ID_SHIFT = 8 + ######################################################################################################################## # Enums ######################################################################################################################## @@ -118,11 +119,11 @@ def parse(cls, data: bytes, offset: int = 0) -> "CmdHeader": :param data: Input data as bytes :param offset: The offset of input data :return: CMDHeader object - :raise Exception: raised when size is incorrect + :raises SPSDKError: raised when size is incorrect :raises SPSDKError: Raised when CRC is incorrect """ if calcsize(cls.FORMAT) > len(data) - offset: - raise Exception() + raise SPSDKError("Incorrect size") obj = cls(EnumCmdTag.NOP) (crc, obj.tag, obj.flags, obj.address, obj.count, obj.data) = unpack_from( cls.FORMAT, data, offset diff --git a/spsdk/sbfile/sb2/headers.py b/spsdk/sbfile/sb2/headers.py index 626d94b6..710f25de 100644 --- a/spsdk/sbfile/sb2/headers.py +++ b/spsdk/sbfile/sb2/headers.py @@ -168,7 +168,7 @@ def parse(cls, data: bytes, offset: int = 0) -> "ImageHeaderV2": :param data: binary representation :param offset: to start parsing data :return: parsed instance of the header - :raise SPSDKError: Unable to parse data + :raises SPSDKError: Unable to parse data """ if cls.SIZE > len(data) - offset: raise SPSDKError("Insufficient amount of data") diff --git a/spsdk/sbfile/sb2/images.py b/spsdk/sbfile/sb2/images.py index 1d03b9d9..f4e6590b 100644 --- a/spsdk/sbfile/sb2/images.py +++ b/spsdk/sbfile/sb2/images.py @@ -13,13 +13,14 @@ from typing import Iterator, List, Optional from spsdk import SPSDKError +from spsdk.apps.utils import get_key from spsdk.crypto.loaders import load_certificate_as_bytes from spsdk.utils.crypto import CertBlockV2, Counter, crypto_backend from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.crypto.backend_internal import internal_backend from spsdk.utils.crypto.certificate import Certificate from spsdk.utils.crypto.common import calc_cypher_block_count -from spsdk.utils.misc import find_first, load_binary, write_file +from spsdk.utils.misc import find_first, load_binary, load_text, write_file from . import sb_21_helper as elf2sb_helper21 from . import sly_bd_parser as bd_parser @@ -394,11 +395,11 @@ def parse(cls, data: bytes, offset: int = 0, kek: bytes = bytes()) -> "BootImage :param offset: The offset of input data :param kek: The Key for unwrapping DEK and MAC keys (required) :return: parsed image object - :raise Exception: raised when header is in wrong format - :raise Exception: raised when there is invalid header version - :raise Exception: raised when signature is incorrect + :raises SPSDKError: raised when header is in wrong format + :raises SPSDKError: raised when there is invalid header version + :raises SPSDKError: raised when signature is incorrect :raises SPSDKError: Raised when kek is empty - :raises Exception: raised when header's nonce is not present + :raises SPSDKError: raised when header's nonce is not present """ if not kek: raise SPSDKError("kek cannot be empty") @@ -414,11 +415,11 @@ def parse(cls, data: bytes, offset: int = 0, kek: bytes = bytes()) -> "BootImage mac = key_blob_unwrap[32:] header_mac_data_calc = crypto_backend().hmac(mac, header_raw_data) if header_mac_data != header_mac_data_calc: - raise Exception() + raise SPSDKError("Invalid header MAC data") # Parse Header header = ImageHeaderV2.parse(header_raw_data) if header.version != "2.0": - raise Exception(f"Invalid Header Version: {header.version} instead 2.0") + raise SPSDKError(f"Invalid Header Version: {header.version} instead 2.0") image_size = header.image_blocks * 16 # Initialize counter if not header.nonce: @@ -447,7 +448,7 @@ def parse(cls, data: bytes, offset: int = 0, kek: bytes = bytes()) -> "BootImage if not cert_sect.cert_block.verify_data( data[offset + image_size :], data[offset : offset + image_size] ): - raise Exception() + raise SPSDKError("Parsing Certification section failed") # Parse Boot Sections while index < (image_size + offset): boot_section = BootSectionV2.parse(data, index, dek=dek, mac=mac, counter=counter) @@ -771,10 +772,10 @@ def parse( :param plain_sections: Sections are not encrypted; this is used only for debugging, not supported by ROM code :return: BootImageV21 parsed object - :raises Exception: raised when header is in incorrect format - :raises Exception: raised when signature is incorrect + :raises SPSDKError: raised when header is in incorrect format + :raises SPSDKError: raised when signature is incorrect :raises SPSDKError: Raised when kek is empty - :raises Exception: raised when header's nonce not present" + :raises SPSDKError: raised when header's nonce not present" """ if not kek: raise SPSDKError("kek cannot be empty") @@ -791,7 +792,7 @@ def parse( # Parse Header header = ImageHeaderV2.parse(header_raw_data) if header.offset_to_certificate_block != (index - offset): - raise Exception() + raise SPSDKError("Invalid offset") # Parse Certificate Block cert_block = CertBlockV2.parse(data, index) index += cert_block.raw_size @@ -808,7 +809,7 @@ def parse( ) if not result: - raise Exception() + raise SPSDKError("Verification failed") # Check flags, if 0x8000 bit is set, the SB file contains SHA-256 between # certificate and signature. if header.flags & BootImageV21.FLAGS_SHA_PRESENT_BIT: @@ -819,7 +820,7 @@ def parse( # Not implemented yet # hmac_data_calc = crypto_backend().hmac(mac, data[index + CmdHeader.SIZE: index + CmdHeader.SIZE + ((2) * 32)]) # if hmac_data != hmac_data_calc: - # raise Exception() + # raise SPSDKError("HMAC failed") if not header.nonce: raise SPSDKError("Header's nonce not present") counter = Counter(header.nonce) @@ -883,8 +884,7 @@ def generate_SB21( # pylint: disable=invalid-name """ # Create lexer and parser, load the BD file content and parse it for # further execution - the parsed BD file is a dictionary in JSON format - with open(str(bd_file_path)) as bd_file: - bd_file_content = bd_file.read() + bd_file_content = load_text(bd_file_path) parser = bd_parser.BDParser() @@ -950,10 +950,7 @@ def generate_SB21( # pylint: disable=invalid-name logger.warning("No KEK key provided, using a zero KEK key") sb_kek = bytes.fromhex("0" * 64) else: - with open(str(key_file_path)) as kek_key_file: - # TODO maybe we should validate the key length and content, to make - # sure the key provided in the file is valid?? - sb_kek = bytes.fromhex(kek_key_file.readline()) + sb_kek = get_key(key_file_path, expected_size=32) # validate keyblobs and perform appropriate actions keyblobs = parsed_bd_file.get("keyblobs", []) diff --git a/spsdk/sbfile/sb2/sections.py b/spsdk/sbfile/sb2/sections.py index a0e5ccbd..e96c674d 100644 --- a/spsdk/sbfile/sb2/sections.py +++ b/spsdk/sbfile/sb2/sections.py @@ -131,7 +131,7 @@ def export( :param counter: The counter object (required) :param dbg_info: Optional[List[str]] optional list to export debug information about content in text format :return: exported bytes - :raise SPSDKError: raised when dek, mac, counter have invalid format + :raises SPSDKError: raised when dek, mac, counter have invalid format """ cmd_dbg_info: Optional[List[str]] = None if dbg_info is not None: @@ -214,7 +214,7 @@ def parse( :param mac: The MAC value in bytes (required) :param counter: The counter object (required) :return: exported bytes - :raise SPSDKError: raised when dek, mac, counter have invalid format + :raises SPSDKError: raised when dek, mac, counter have invalid format """ if not isinstance(dek, bytes): raise SPSDKError("Invalid type of dek, should be bytes") @@ -228,7 +228,7 @@ def parse( offset += CmdHeader.SIZE + cls.HMAC_SIZE # Check header HMAC if header_hmac_data != crypto_backend().hmac(mac, header_encrypted): - raise SPSDKError() + raise SPSDKError("Invalid header HMAC") # Decrypt header header_decrypted = crypto_backend().aes_ctr_decrypt(dek, header_encrypted, counter.value) counter.increment() @@ -249,7 +249,7 @@ def parse( block_size = section_size hmac_block = crypto_backend().hmac(mac, data[offset : offset + block_size]) if hmac_block != hmac_data[hmac_index : hmac_index + cls.HMAC_SIZE]: - raise SPSDKError() + raise SPSDKError("HMAC failed") hmac_count -= 1 hmac_index += cls.HMAC_SIZE section_size -= block_size @@ -324,15 +324,15 @@ def export( :param mac: The MAC value in bytes (required) :param counter: The counter object (required) :return: exported bytes - :raise Exception: raised when dek, mac, counter have invalid format + :raises SPSDKError: raised when dek, mac, counter have invalid format :raises SPSDKError: Raised size of exported bytes is invalid """ if not isinstance(dek, bytes): - raise Exception() + raise SPSDKError("DEK value is not in bytes") if not isinstance(mac, bytes): - raise Exception() + raise SPSDKError("MAC value is not in bytes") if not isinstance(counter, Counter): - raise Exception() + raise SPSDKError("Counter value is not incorrect") # Prepare Header data header_data = self._header.export() header_encrypted = crypto_backend().aes_ctr_encrypt(dek, header_data, counter.value) diff --git a/spsdk/sbfile/sb2/sly_bd_parser.py b/spsdk/sbfile/sb2/sly_bd_parser.py index 91bf70c3..d49f8970 100644 --- a/spsdk/sbfile/sb2/sly_bd_parser.py +++ b/spsdk/sbfile/sb2/sly_bd_parser.py @@ -537,16 +537,20 @@ def section_option_list(self, token: YaccProduction) -> Dict: # type: ignore :param token: object holding the content defined in decorator. :return: dictionary holding the content of section options. """ - return token.section_option_list.update(token.section_option) + options = {} + options.update(token.section_option) + if token.section_option_list: + token.section_option_list.append(options) + return token.section_option_list @_("section_option") # type: ignore - def section_option_list(self, token: YaccProduction) -> Dict: # type: ignore + def section_option_list(self, token: YaccProduction) -> List: # type: ignore """Parser rule. :param token: object holding the content defined in decorator. :return: dictionary holding a section option. """ - return token.section_option + return [token.section_option] @_("IDENT ASSIGN const_expr") # type: ignore def section_option(self, token: YaccProduction) -> Dict: # type: ignore @@ -555,7 +559,6 @@ def section_option(self, token: YaccProduction) -> Dict: # type: ignore :param token: object holding the content defined in decorator. :return: dictionary holding the content of a section option. """ - self.error(token, ": section options are not supported") return {token.IDENT: token.const_expr} @_("LBRACE statement RBRACE") # type: ignore diff --git a/spsdk/sbfile/sb31/commands.py b/spsdk/sbfile/sb31/commands.py index 7d923479..0995d330 100644 --- a/spsdk/sbfile/sb31/commands.py +++ b/spsdk/sbfile/sb31/commands.py @@ -1017,6 +1017,7 @@ def load_from_config( "checkFwVersion": CmdFwVersionCheck, } + ######################################################################################################################## # Command parser from raw data ######################################################################################################################## diff --git a/spsdk/sbfile/sb31/images.py b/spsdk/sbfile/sb31/images.py index 32b7160c..8f2642be 100644 --- a/spsdk/sbfile/sb31/images.py +++ b/spsdk/sbfile/sb31/images.py @@ -6,24 +6,30 @@ # SPDX-License-Identifier: BSD-3-Clause """Module used for generation SecureBinary V3.1.""" import logging +import os +from copy import deepcopy from datetime import datetime from struct import calcsize, pack, unpack_from from typing import Any, Dict, List, Optional from spsdk import SPSDKError -from spsdk.crypto.loaders import load_private_key_from_data -from spsdk.image import MBIMG_SCH_FILE, SB3_SCH_FILE +from spsdk.crypto.signature_provider import SignatureProvider, get_signature_provider +from spsdk.image import IMG_DATA_FOLDER, MBIMG_SCH_FILE from spsdk.sbfile.sb31.commands import CFG_NAME_TO_CLASS, CmdSectionHeader, MainCmd from spsdk.sbfile.sb31.functions import KeyDerivator from spsdk.utils.crypto import CRYPTO_SCH_FILE from spsdk.utils.crypto.abstract import BaseClass from spsdk.utils.crypto.backend_internal import internal_backend from spsdk.utils.crypto.cert_blocks import CertBlockV31 -from spsdk.utils.misc import align_block, load_binary, load_text, value_to_int +from spsdk.utils.database import Database +from spsdk.utils.misc import align_block, load_text, value_to_int from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas logger = logging.getLogger(__name__) +SB3_SCH_FILE: str = os.path.join(IMG_DATA_FOLDER, "sch_sb3.yaml") +DATABASE_FILE = os.path.join(IMG_DATA_FOLDER, "database_sb31.yaml") + ######################################################################################################################## # Secure Boot Image Class (Version 3.1) @@ -381,7 +387,7 @@ def __init__( curve_name: str, cert_block: CertBlockV31, firmware_version: int, - signing_key: bytes, + signature_provider: SignatureProvider, pck: Optional[bytes] = None, kdk_access_rights: Optional[int] = None, description: Optional[str] = None, @@ -395,7 +401,7 @@ def __init__( :param curve_name: Name of the ECC curve used for Secure binary (secp256r1/secp384r1). :param cert_block: Certification block. :param firmware_version: Firmware version (must be bigger than current CMPA record). - :param signing_key: Key to final sign of SB3.1 image. + :param signature_provider: Signature provider for final sign of SB3.1 image. :param pck: Part Common Key (needed if `is_encrypted` is True), defaults to None :param kdk_access_rights: Key Derivation Key access rights (needed if `is_encrypted` is True), defaults to None :param description: Custom description up to 16 characters long, defaults to None @@ -416,7 +422,7 @@ def __init__( self.description = description self.is_nxp_container = is_nxp_container self.flags = flags - self.signing_key = signing_key + self.signature_provider = signature_provider self.sb_header = SecureBinary31Header( firmware_version=self.firmware_version, @@ -442,12 +448,43 @@ def _get_prv_key_length(curve_name: str) -> int: """Get size of key for curve in bits.""" return {"secp256r1": 256, "secp384r1": 384}[curve_name] + @classmethod + def get_validation_schemas_family(cls) -> List[Dict[str, Any]]: + """Create the validation schema just for supported families. + + :return: List of validation schemas for SB31 supported families. + """ + sch_cfg = ValidationSchemas.get_schema_file(SB3_SCH_FILE) + sch_cfg["sb3_family"]["properties"]["family"]["enum"] = cls.get_supported_families() + return [sch_cfg["sb3_family"]] + + @classmethod + def get_commands_validation_schemas(cls, family: str) -> List[Dict[str, Any]]: + """Create the list of validation schemas. + + :param family: Family description. + :return: List of validation schemas. + """ + sb3_sch_cfg = ValidationSchemas().get_schema_file(SB3_SCH_FILE) + + schemas: List[Dict[str, Any]] = [deepcopy(sb3_sch_cfg["sb3_commands"])] + + # remove unused command for current family + supported_commands = Database(DATABASE_FILE).get_device_value("supported_commands", family) + list_of_commands: List[Dict] = schemas[0]["properties"]["commands"]["items"]["oneOf"] + for command in list_of_commands: + if list(command["properties"].keys())[0] not in supported_commands: + list_of_commands.remove(command) + + return schemas + @classmethod def get_validation_schemas( - cls, include_test_configuration: bool = False + cls, family: str, include_test_configuration: bool = False ) -> List[Dict[str, Any]]: """Create the list of validation schemas. + :param family: Family description. :param include_test_configuration: Add also testing configuration schemas. :return: List of validation schemas. """ @@ -455,27 +492,29 @@ def get_validation_schemas( crypto_sch_cfg = ValidationSchemas().get_schema_file(CRYPTO_SCH_FILE) sb3_sch_cfg = ValidationSchemas().get_schema_file(SB3_SCH_FILE) - ret: List[Dict[str, Any]] = [] - ret.extend( + schemas: List[Dict[str, Any]] = [] + schemas.extend( [ mbi_sch_cfg[x] for x in [ "firmware_version", - "use_isk", - "signing_cert_prv_key", - "signing_root_prv_key", "signing_prv_key_lpc55s3x", "elliptic_curves", ] ] ) - ret.extend([crypto_sch_cfg[x] for x in ["certificate_v31", "certificate_root_keys"]]) - ret.extend( - [sb3_sch_cfg[x] for x in ["sb3_family", "sb3", "sb3_description", "sb3_commands"]] - ) + schemas.extend([crypto_sch_cfg[x] for x in ["certificate_v31", "certificate_root_keys"]]) + schemas.extend([sb3_sch_cfg[x] for x in ["sb3_family", "sb3", "sb3_description"]]) + schemas.extend(cls.get_commands_validation_schemas(family)) if include_test_configuration: - ret.append(sb3_sch_cfg["sb3_test"]) - return ret + schemas.append(sb3_sch_cfg["sb3_test"]) + + # find family + for schema in schemas: + if "properties" in schema and "family" in schema["properties"]: + schema["properties"]["family"]["enum"] = cls.get_supported_families() + break + return schemas @classmethod def load_from_config( @@ -516,10 +555,13 @@ def load_from_config( ) assert curve_name and isinstance(curve_name, str) assert signing_key_path - signing_key = ( - load_binary(signing_key_path, search_paths=search_paths) if signing_key_path else None + signature_provider = get_signature_provider( + sp_cfg=config.get("signProvider"), + local_file_key=signing_key_path, + search_paths=search_paths, + mode="deterministic-rfc6979", ) - assert signing_key + assert signature_provider pck = None if is_encrypted: @@ -539,7 +581,7 @@ def load_from_config( description=description, is_nxp_container=is_nxp_container, flags=container_configuration_word, - signing_key=signing_key, + signature_provider=signature_provider, timestamp=timestamp, is_encrypted=is_encrypted, ) @@ -554,36 +596,36 @@ def validate(self) -> None: :raises SPSDKError: Invalid configuration of SB3.1 class members. """ - if self.signing_key is None or not isinstance(self.signing_key, bytes): - raise SPSDKError(f"SB3.1 Signing Key is invalid: {self.signing_key}") + if self.signature_provider is None or not isinstance( + self.signature_provider, SignatureProvider + ): + raise SPSDKError(f"SB3.1 signature provider is invalid: {self.signature_provider}") try: prv_key_length = self._get_prv_key_length(self.curve_name) except KeyError as exc: raise SPSDKError(f"Invalid SB3 curve name: ({self.curve_name})") from exc - try: - prv_key = load_private_key_from_data(self.signing_key) - except SPSDKError as exc: - raise SPSDKError(f"Invalid SB3 Signing key: ({str(exc)})") from exc - - if prv_key.key_size != prv_key_length: + if (self.signature_provider.signature_length / 2) * 8 != prv_key_length: raise SPSDKError( - f"Invalid length of SB3.1 signing key({len(self.signing_key)} != {prv_key_length})" + f"Invalid length of SB3.1 signing key({self.signature_provider.signature_length} != {prv_key_length})" f" for used curve: {self.curve_name}!" ) self.cert_block.validate() self.sb_header.validate() self.sb_commands.validate() - def export(self) -> bytes: + def export(self, cert_block: Optional[bytes] = None) -> bytes: """Generate binary output of SB3.1 file. :return: Content of SB3.1 file in bytes. """ self.validate() - cert_block_data = self.cert_block.export() + if cert_block: + cert_block_data = cert_block + else: + cert_block_data = self.cert_block.export() sb3_commands_data = self.sb_commands.export() final_data = bytes() @@ -596,7 +638,7 @@ def export(self) -> bytes: final_data += cert_block_data # SIGNATURE - final_data += internal_backend.ecc_sign(self.signing_key, final_data) + final_data += self.signature_provider.sign(final_data) # COMMANDS BLOBS DATA final_data += sb3_commands_data @@ -625,10 +667,7 @@ def get_supported_families() -> List[str]: :return: List of supported families. """ - sb3_sch_cfg = ValidationSchemas().get_schema_file(SB3_SCH_FILE) - sb3_families = sb3_sch_cfg["sb3_family"] - - return sb3_families["properties"]["family"]["enum"] + return Database(DATABASE_FILE).devices.device_names @classmethod def generate_config_template(cls, family: str) -> Dict[str, str]: @@ -640,7 +679,7 @@ def generate_config_template(cls, family: str) -> Dict[str, str]: ret: Dict[str, str] = {} if family in cls.get_supported_families(): - schemas = cls.get_validation_schemas() + schemas = cls.get_validation_schemas(family) schemas.append(ValidationSchemas.get_schema_file(SB3_SCH_FILE)["sb3_output"]) override = {} override["family"] = family diff --git a/spsdk/sdp/interfaces/uart.py b/spsdk/sdp/interfaces/uart.py index 1d3385c3..9ada891d 100644 --- a/spsdk/sdp/interfaces/uart.py +++ b/spsdk/sdp/interfaces/uart.py @@ -13,8 +13,10 @@ from serial import Serial, SerialTimeoutException from serial.tools.list_ports import comports +from spsdk.exceptions import SPSDKError from spsdk.sdp.commands import CmdPacket, CmdResponse from spsdk.sdp.exceptions import SdpConnectionError +from spsdk.utils.exceptions import SPSDKTimeoutError from .base import SDPInterface @@ -165,7 +167,7 @@ def _read(self, length: int) -> bytes: try: data = self.device.read(length) if not data: - raise Exception("No response from SDP device.") + raise SPSDKError("No response from SDP device.") logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>") return data except Exception as e: @@ -175,7 +177,7 @@ def _write(self, data: bytes) -> None: """Send data to device. :param data: Data to send - :raises TimeoutError: when sendig of data times-out + :raises SPSDKTimeoutError: when sending of data times-out :raises SdpConnectionError: when send data to device fails """ logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]") @@ -185,7 +187,7 @@ def _write(self, data: bytes) -> None: self.device.write(data) self.device.flush() except SerialTimeoutException as e: - raise TimeoutError( + raise SPSDKTimeoutError( f"Write timeout error. The timeout is set to {self.device.write_timeout} s. Consider increasing it." ) from e except Exception as e: diff --git a/spsdk/sdp/interfaces/usb.py b/spsdk/sdp/interfaces/usb.py index e70f25ce..b3c01aa3 100644 --- a/spsdk/sdp/interfaces/usb.py +++ b/spsdk/sdp/interfaces/usb.py @@ -137,6 +137,8 @@ def _decode_report(raw_data: bytes) -> CmdResponse: :param raw_data: Data received :return: CmdResponse object """ + if not raw_data: + raise SdpConnectionError("No data were received") logger.debug(f"IN [{len(raw_data)}]: {', '.join(f'{b:02X}' for b in raw_data)}") return CmdResponse(raw_data[0] == HID_REPORT["HAB"][0], raw_data[1:]) diff --git a/spsdk/shadowregs/shadowregs.py b/spsdk/shadowregs/shadowregs.py index e99abdb6..0ae7d3a0 100644 --- a/spsdk/shadowregs/shadowregs.py +++ b/spsdk/shadowregs/shadowregs.py @@ -5,8 +5,8 @@ # # SPDX-License-Identifier: BSD-3-Clause """The shadow registers control DAT support file.""" + import logging -import os from typing import Any, List, Optional from ruamel.yaml import YAML @@ -17,9 +17,16 @@ from spsdk.dat.dm_commands import StartDebugSession from spsdk.debuggers.debug_probe import DebugProbe, SPSDKDebugProbeError from spsdk.debuggers.utils import test_ahb_access -from spsdk.utils.misc import change_endianness, value_to_bytes, value_to_int +from spsdk.utils.misc import ( + change_endianness, + load_configuration, + value_to_bytes, + value_to_int, + write_file, +) from spsdk.utils.reg_config import RegConfig from spsdk.utils.registers import Registers, RegsRegister, SPSDKRegsErrorRegisterNotFound +from spsdk.utils.schema_validator import ConfigTemplate logger = logging.getLogger(__name__) @@ -91,7 +98,8 @@ def _write_shadow_reg(self, addr: int, data: int, verify_mask: int = 0) -> None: read_back = self.probe.mem_reg_read(addr) if read_back & verify_mask != data & verify_mask: raise IoVerificationError( - f"The written data 0x{data:08X} to 0x{addr:08X} address are invalid." + f"The verification of written shadow register 0x{addr:08X} failed." + " Maybe a READ LOCK is set for that register." ) def reload_registers(self) -> None: @@ -99,14 +107,13 @@ def reload_registers(self) -> None: 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.""" + def sets_all_registers(self, verify: bool = True) -> None: + """Update all shadow registers in target by local values. + + :param verify: Verity write operation. + """ for reg in self.regs.get_registers(): - if reg.has_group_registers(): - for subreg in reg.sub_regs: - self.set_register(subreg.name, subreg.get_value()) - else: - self.set_register(reg.name, reg.get_value()) + self.set_register(reg.name, reg.get_value(), verify) def reload_register(self, reg: RegsRegister) -> None: """Reload the value in requested register. @@ -122,12 +129,14 @@ def read_register(self, reg: RegsRegister) -> bytes: """ return bytes(self.get_register(reg.name)) - def set_register(self, reg_name: str, data: Any) -> None: + def set_register(self, reg_name: str, data: Any, verify: bool = True) -> None: """The function sets the value of the specified register. - param reg: The register name. - param data: The new data to be stored to shadow register. - raises SPSDKDebugProbeError: The debug probe is not specified. + :param reg_name: The register name. + :param data: The new data to be stored to shadow register. + :param verify: Verity write operation. + :raises SPSDKDebugProbeError: The debug probe is not specified. + :raises SPSDKError: General error with write of Shadow register. """ def write_reg(base_address: int, reg: RegsRegister) -> None: @@ -145,13 +154,14 @@ def write_reg(base_address: int, reg: RegsRegister) -> None: if reg.reverse: value = int.from_bytes(change_endianness(value_to_bytes(value)), "big") # Create verify mask - bitfields = reg.get_bitfields() verify_mask = 0 - if bitfields: - for bitfield in bitfields: - verify_mask = verify_mask | (((1 << bitfield.width) - 1) << bitfield.offset) - else: - verify_mask = (1 << reg.width) - 1 + if verify: + bitfields = reg.get_bitfields() + if bitfields: + for bitfield in bitfields: + verify_mask = verify_mask | (((1 << bitfield.width) - 1) << bitfield.offset) + else: + verify_mask = (1 << reg.width) - 1 self._write_shadow_reg( addr=base_address + reg.offset, data=value, verify_mask=verify_mask @@ -161,7 +171,7 @@ def write_reg(base_address: int, reg: RegsRegister) -> None: reg = self.regs.find_reg(reg_name, include_group_regs=True) reg.set_value(data, raw=True) if reg.has_group_registers(): - for sub_reg in reg.sub_regs: + for sub_reg in reg.sub_regs[:: -1 if reg.reverse_subregs_order else 1]: write_reg(self.offset, sub_reg) else: write_reg(self.offset, reg=reg) @@ -196,8 +206,7 @@ def read_reg(base_address: int, reg: RegsRegister) -> bytes: ret = bytearray() if reg.has_group_registers(): - - for sub_reg in reg.sub_regs: + for sub_reg in reg.sub_regs[:: -1 if reg.reverse_subregs_order else 1]: ret.extend(read_reg(self.offset, sub_reg)) else: ret.extend(read_reg(self.offset, reg=reg)) @@ -244,7 +253,7 @@ def add_reg(reg: RegsRegister) -> str: ) from exc if reg.has_group_registers(): - for sub_reg in reg.sub_regs: + for sub_reg in reg.sub_regs[:: -1 if reg.reverse_subregs_order else 1]: ret += add_reg(sub_reg) else: ret += add_reg(reg) @@ -252,7 +261,7 @@ def add_reg(reg: RegsRegister) -> str: self.fuse_mode = False return ret - def create_yml_config(self, file_name: str, raw: bool = False, diff: bool = False) -> None: + def create_yaml_config(self, file_name: str, raw: bool = False, diff: bool = False) -> None: """The function creates the configuration YML file. :param file_name: The file_name (without extension) of stored configuration. @@ -284,14 +293,9 @@ def create_yml_config(self, file_name: str, raw: bool = False, diff: bool = Fals indent=2, diff=diff, ) + write_file(ConfigTemplate.convert_cm_to_yaml(data), file_name, encoding="utf8") - if not os.path.exists(os.path.dirname(file_name)): - os.makedirs(os.path.dirname(file_name)) - - 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: + def load_yaml_config(self, file_name: str, raw: bool = False) -> None: """The function loads the configuration from YML file. :param file_name: The file_name (without extension) of stored configuration. @@ -300,14 +304,7 @@ def load_yml_config(self, file_name: str, raw: bool = False) -> None: """ 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", encoding="utf8") as yml_config_file: - yaml = YAML() - yaml.indent(sequence=4, offset=2) - data = yaml.load(yml_config_file) - except FileNotFoundError as exc: - raise SPSDKError("File with YML configuration doesn't exists.") from exc - + data = load_configuration(file_name) self.regs.load_yml_config(data["registers"], antipole_regs, computed_fields) if not raw: # Just update only configured registers diff --git a/spsdk/tp/adapters/tpdev_scard.py b/spsdk/tp/adapters/tpdev_scard.py index a1af32e6..864b8bfa 100644 --- a/spsdk/tp/adapters/tpdev_scard.py +++ b/spsdk/tp/adapters/tpdev_scard.py @@ -425,8 +425,7 @@ def _serialize_cert( cert_file_path: str, search_dirs: Optional[List[str]] = None, destination: int = 0 ) -> bytes: cert_file = find_file(cert_file_path, search_paths=search_dirs) - with open(cert_file, "rb") as f: - cert_data = f.read() + cert_data = load_binary(cert_file) return TpDevSmartCard._serialize_cert_data(cert_data, destination) @staticmethod diff --git a/spsdk/tp/tphost.py b/spsdk/tp/tphost.py index 4985aed1..d73266c3 100644 --- a/spsdk/tp/tphost.py +++ b/spsdk/tp/tphost.py @@ -112,7 +112,7 @@ def load_provisioning_fw( self.info_print("1.4.Step - Checking whether provisioning firmware booted.") self.tptarget.open() if not self.tptarget.check_provisioning_firmware(): - raise SPSDKError() + raise SPSDKError("Provisioning firmware did not boot properly") if keep_target_open and not self.tptarget.is_open: self.tptarget.open() @@ -230,8 +230,7 @@ def do_provisioning( logger.info(f"TP Challenge:\n{Container.parse(challenge)}") if save_debug_data: - with open("x_challenge.bin", "wb") as f: - f.write(challenge) + write_file(challenge, "x_challenge.bin", "wb") self.info_print("3.Step - Prove a genuinity in TP target.") tp_data = self.tptarget.prove_genuinity_challenge( @@ -240,8 +239,7 @@ def do_provisioning( logger.info(f"TP Response:\n{Container.parse(tp_data)}") if save_debug_data: - with open("x_tp_response.bin", "wb") as f: - f.write(tp_data) + write_file(tp_data, "x_tp_response.bin", "wb") self.info_print("4.Step - Authenticate TP response from TP target.") wrapped_data = self.tpdev.authenticate_response( @@ -250,8 +248,7 @@ def do_provisioning( logger.info(f"TP ISP WRAPPED DATA:\n{Container.parse(wrapped_data)}") if save_debug_data: - with open("x_wrapped_data.bin", "wb") as f: - f.write(wrapped_data) + write_file(wrapped_data, "x_wrapped_data.bin", "wb") self.info_print("5.Step - Create Audit Log record.") self.create_audit_log_record(wrapped_data, audit_log) @@ -463,7 +460,7 @@ def get_tp_response( challenge = challenge or secrets.token_bytes(16) if len(challenge) != 16: - raise SPSDKTpError(f"Challenge has to be 16B long") + raise SPSDKTpError("Challenge has to be 16B long") challenge_container = Container() challenge_container.add_entry( diff --git a/spsdk/tp/utils.py b/spsdk/tp/utils.py index 0a99a05b..d7e701d3 100644 --- a/spsdk/tp/utils.py +++ b/spsdk/tp/utils.py @@ -8,9 +8,9 @@ import os from typing import List, Optional, Type -from ruamel.yaml import YAML - from spsdk.crypto import ec +from spsdk.exceptions import SPSDKError +from spsdk.utils.misc import load_configuration from . import TP_DATA_FOLDER, TpDevInterface, TpIntfDescription, TpTargetInterface from .adapters import TP_DEVICES, TP_TARGETS @@ -31,8 +31,7 @@ def get_supported_devices() -> List[str]: :return: List of devices. """ - with open(os.path.join(TP_DATA_FOLDER, "database.yaml")) as f: - data = YAML(typ="safe").load(f) + data = load_configuration(os.path.join(TP_DATA_FOLDER, "database.yaml")) return list(data["devices"].keys()) @@ -49,10 +48,10 @@ def scan_tp_devices( :param tpdev: Selection of one type of TP device, defaults to None (scan all supported). :param settings: Additional settings to setup interface, defaults to {}. :return: List of active TP device descriptors. - :raises ValueError: Invalid value of parameter. + :raises SPSDKError: Invalid value of parameter. """ if tpdev and tpdev not in get_tp_device_types(): - raise ValueError(f"Unsupported TP device name - {tpdev}") + raise SPSDKError(f"Unsupported TP device name - {tpdev}") dev_list = [tpdev] if tpdev else get_tp_device_types() @@ -102,10 +101,10 @@ def scan_tp_targets( :param tptarget: Selection of one type of TP target, defaults to None (scan all supported). :param settings: Additional settings to setup interface, defaults to {}. :return: List of active TP devices. - :raises ValueError: Invalid value of parameter. + :raises SPSDKError: Invalid value of parameter. """ if tptarget and tptarget not in get_tp_target_types(): - raise ValueError(f"Unsupported TP device name - {tptarget}") + raise SPSDKError(f"Unsupported TP device name - {tptarget}") target_list = [tptarget] if tptarget else get_tp_target_types() diff --git a/spsdk/utils/crypto/__init__.py b/spsdk/utils/crypto/__init__.py index d70c9e38..912a6ca2 100644 --- a/spsdk/utils/crypto/__init__.py +++ b/spsdk/utils/crypto/__init__.py @@ -11,14 +11,14 @@ from spsdk.utils import UTILS_DATA_FOLDER -CRYPTO_SCH_FILE: str = os.path.join(UTILS_DATA_FOLDER, "sch_crypto.yml") +CRYPTO_SCH_FILE: str = os.path.join(UTILS_DATA_FOLDER, "sch_crypto.yaml") OTFAD_DATA_FOLDER: str = os.path.join(UTILS_DATA_FOLDER, "otfad") -OTFAD_SCH_FILE: str = os.path.join(OTFAD_DATA_FOLDER, "sch_otfad.yml") -OTFAD_DATABASE_FILE: str = os.path.join(OTFAD_DATA_FOLDER, "database.yml") +OTFAD_SCH_FILE: str = os.path.join(OTFAD_DATA_FOLDER, "sch_otfad.yaml") +OTFAD_DATABASE_FILE: str = os.path.join(OTFAD_DATA_FOLDER, "database.yaml") IEE_DATA_FOLDER: str = os.path.join(UTILS_DATA_FOLDER, "iee") -IEE_SCH_FILE: str = os.path.join(IEE_DATA_FOLDER, "sch_iee.yml") -IEE_DATABASE_FILE: str = os.path.join(IEE_DATA_FOLDER, "database.yml") +IEE_SCH_FILE: str = os.path.join(IEE_DATA_FOLDER, "sch_iee.yaml") +IEE_DATABASE_FILE: str = os.path.join(IEE_DATA_FOLDER, "database.yaml") from .abstract import BackendClass diff --git a/spsdk/utils/crypto/backend_internal.py b/spsdk/utils/crypto/backend_internal.py index 39cb9ed8..03ef37a8 100644 --- a/spsdk/utils/crypto/backend_internal.py +++ b/spsdk/utils/crypto/backend_internal.py @@ -331,30 +331,32 @@ def ecc_sign( def ecc_verify( self, - key: Union[ECC.EccKey, bytes], # TODO - could we renamed abstract class to "key" only? + public_key: Union[ECC.EccKey, bytes], signature: bytes, data: bytes, algorithm: Optional[str] = None, ) -> bool: """Verify (EC)DSA signature. - :param key: ECC private or public key, either as EccKey or bytes + :param public_key: ECC private or public key, either as EccKey or bytes :param signature: Signature to verify, r and s coordinates as bytes :param data: Data to validate :param algorithm: Hash algorithm, if None the hash length is determined from ECC curve size :return: True if the signature is valid :raises SPSDKError: Signature length is invalid """ - key = key if isinstance(key, ECC.EccKey) else ECC.import_key(key) - hash_name = algorithm or f"sha{key.pointQ.size_in_bits()}" - coordinate_size = key.pointQ.size_in_bytes() + public_key = ( + public_key if isinstance(public_key, ECC.EccKey) else ECC.import_key(public_key) + ) + hash_name = algorithm or f"sha{public_key.pointQ.size_in_bits()}" + coordinate_size = public_key.pointQ.size_in_bytes() if len(signature) != 2 * coordinate_size: raise SPSDKError( f"Invalid signature size: expected {2 * coordinate_size}, actual: {len(signature)}" ) hasher = self._get_algorithm(name=hash_name, data=data) try: - DSS.new(key, mode="deterministic-rfc6979").verify(hasher, signature) + DSS.new(public_key, mode="deterministic-rfc6979").verify(hasher, signature) return True except ValueError: return False diff --git a/spsdk/utils/crypto/cert_blocks.py b/spsdk/utils/crypto/cert_blocks.py index 609a5a29..bdc47b04 100644 --- a/spsdk/utils/crypto/cert_blocks.py +++ b/spsdk/utils/crypto/cert_blocks.py @@ -7,24 +7,29 @@ """Module for handling Certificate block.""" +import datetime import logging +import os import re from struct import calcsize, pack, unpack_from from typing import Any, Dict, List, Optional, Sequence, Union from Crypto.PublicKey import ECC +from ruamel.yaml import CommentedMap as CM -from spsdk import SPSDKError +from spsdk import SPSDKError, crypto +from spsdk.crypto import loaders from spsdk.crypto.loaders import load_certificate_as_bytes +from spsdk.crypto.signature_provider import SignatureProvider, get_signature_provider from spsdk.exceptions import SPSDKValueError from spsdk.utils import misc from spsdk.utils.crypto import CRYPTO_SCH_FILE -from spsdk.utils.schema_validator import ValidationSchemas +from spsdk.utils.schema_validator import CommentedConfig, ValidationSchemas from .abstract import BaseClass from .backend_internal import internal_backend from .certificate import Certificate -from .common import crypto_backend +from .common import crypto_backend, get_matching_key_id logger = logging.getLogger(__name__) @@ -99,10 +104,10 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertBlockHeader": :param data: Input data as bytes :param offset: The offset of input data (default: 0) :return: Certificate Header instance - :raises Exception: Unexpected size or signature of data + :raises SPSDKError: Unexpected size or signature of data """ if cls.SIZE > len(data) - offset: - raise Exception() + raise SPSDKError("Incorrect size") ( signature, major_version, @@ -115,9 +120,9 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertBlockHeader": cert_table_length, ) = unpack_from(cls.FORMAT, data, offset) if signature != cls.SIGNATURE: - raise Exception() + raise SPSDKError("Incorrect signature") if length != cls.SIZE: - raise Exception() + raise SPSDKError("Incorrect length") obj = cls( version=f"{major_version}.{minor_version}", flags=flags, @@ -364,7 +369,7 @@ def export(self) -> bytes: # CA: Using a single certificate is allowed. In this case, the sole certificate must be self-signed and must not # be a CA. If multiple certificates are used, the root must be self-signed and all but the last must be CAs. if self._cert[-1].ca: - raise SPSDKError("The last chain certificate must not be CA") + raise SPSDKError("The last chain certificate must not be CA.") if not all(cert.ca for cert in self._cert[:-1]): raise SPSDKError("All certificates except the last chain certificate must be CA") # Export @@ -386,12 +391,12 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertBlockV2": :param data: Binary data :param offset: Offset within the data, where the Certificate block begins, defaults to 0 :return: Certificate Block instance - :raises Exception: Length of the data doesn't match Certificate Block length + :raises SPSDKError: Length of the data doesn't match Certificate Block length """ header = CertBlockHeader.parse(data, offset) offset += CertBlockHeader.SIZE if (len(data) - offset) < (header.cert_table_length + (cls.RKHT_SIZE * cls.RKH_SIZE)): - raise Exception() + raise SPSDKError("Length of the data doesn't match Certificate Block length") obj = cls(version=header.version, flags=header.flags, build_number=header.build_number) for i in range(header.cert_count): cert_len = unpack_from(" List[Dict[str, Any]]: sch_cfg = ValidationSchemas.get_schema_file(CRYPTO_SCH_FILE) return [ sch_cfg["certificate_v2"], - sch_cfg["certificate_v2_chain_id"], sch_cfg["certificate_root_keys"], ] @@ -436,7 +440,9 @@ def from_config( root_certificates[1].append(config.get("rootCertificate1File", None)) root_certificates[2].append(config.get("rootCertificate2File", None)) root_certificates[3].append(config.get("rootCertificate3File", None)) - main_cert_chain_id = get_main_cert_index(config, default=0) + main_cert_chain_id = get_main_cert_index(config, search_paths=search_paths) + if root_certificates[main_cert_chain_id][0] is None: + raise SPSDKError(f"A key rootCertificate{main_cert_chain_id}File must be defined") # get all certificate chain related keys from config pattern = f"chainCertificate{main_cert_chain_id}File[0-3]" @@ -484,7 +490,17 @@ def convert_to_ecc_key(key: Union[ECC.EccKey, bytes]) -> ECC.EccKey: """Convert key into EccKey instance.""" if isinstance(key, ECC.EccKey): return key - return ECC.import_key(key) + try: + return ECC.import_key(key) + except Exception: + pass + # Just recreate public key from the parsed data + coordinate_length = len(key) // 2 + coor_x = int.from_bytes(key[:coordinate_length], byteorder="big") + coor_y = int.from_bytes(key[coordinate_length:], byteorder="big") + curve = "secp256r1" if coordinate_length == 32 else "secp384r1" + ecc_point = ECC.EccPoint(coor_x, coor_y, curve) + return ECC.EccKey(curve=curve, point=ecc_point) class CertificateBlockHeader(BaseClass): @@ -504,8 +520,7 @@ def __init__(self, format_version: str = "2.1") -> None: def info(self) -> str: """Get info of Certificate block header.""" - info = "" - info += f"Format version: {self.format_version}\n" + info = f"Format version: {self.format_version}\n" info += f"Certificate block size: {self.cert_block_size}\n" return info @@ -549,6 +564,10 @@ def parse(cls, data: bytes, offset: int = 0) -> "CertificateBlockHeader": obj.cert_block_size = cert_block_size return obj + def __len__(self) -> int: + """Length of the Certificate block header.""" + return calcsize(self.FORMAT) + class RootKeyRecord(BaseClass): """Create Root key record.""" @@ -558,7 +577,7 @@ class RootKeyRecord(BaseClass): def __init__( self, ca_flag: bool, - root_certs: Union[Sequence[ECC.EccKey], Sequence[bytes]], + root_certs: Optional[Union[Sequence[ECC.EccKey], Sequence[bytes]]] = None, used_root_cert: int = 0, ) -> None: """Constructor for Root key record. @@ -568,22 +587,41 @@ def __init__( :param used_root_cert: Used root cert number 0-3 """ self.ca_flag = ca_flag - self.root_certs = [convert_to_ecc_key(cert) for cert in root_certs] + self.root_certs_input = root_certs + self.root_certs: List[ECC.EccKey] = [] self.used_root_cert = used_root_cert - self.flags = self._calculate_flags() - self.ctrk_hash_table = self._create_ctrk_hash_table() - self.rotkth = self._calculate_rotkth() - self.root_public_key = self._create_root_public_key() + self.flags = 0 + self.ctrk_hash_table = b"" + self.rotkth = b"" + self.root_public_key = b"" + + @property + def number_of_certificates(self) -> int: + """Get number of included certificates.""" + return (self.flags & 0xF0) >> 4 + + @property + def expected_size(self) -> int: + """Get expected binary block size.""" # the '4' means 4 bytes for flags - self.expected_size = 4 + len(self.ctrk_hash_table) + len(self.root_public_key) + return 4 + len(self.ctrk_hash_table) + len(self.root_public_key) def info(self) -> str: """Get info of Root key record.""" + cert_type = {0x1: "NIST P-256", 0x2: "NIST P-384"}[self.flags & 0xF] info = "" - info += f"Flags: {self.flags}\n" - info += f"CA flag: {self.ca_flag}\n" - info += f"Root certs: {self.root_certs}\n" - info += f"Used root cert: {self.used_root_cert}\n" + info += f"Flags: {hex(self.flags)}\n" + info += f" - CA: {bool(self.ca_flag)}, ISK Certificate is {'not ' if self.ca_flag else ''}mandatory\n" + info += f" - Used Root c.:{self.used_root_cert}\n" + info += f" - Number of c.:{self.number_of_certificates}\n" + info += f" - Cert. type: {cert_type}\n" + if self.root_certs: + info += f"Root certs: {self.root_certs}\n" + if self.ctrk_hash_table: + info += f"CTRK Hash table: {self.ctrk_hash_table.hex()}\n" + if self.root_public_key: + info += f"Root public key: {str(convert_to_ecc_key(self.root_public_key))}\n" + return info def _calculate_flags(self) -> int: @@ -613,33 +651,71 @@ def _create_ctrk_hash_table(self) -> bytes: for key in self.root_certs: data_to_hash = get_ecc_key_bytes(key) ctrk_hash = internal_backend.hash( - data=data_to_hash, algorithm=f"sha{key.pointQ.size_in_bits()}" + data=data_to_hash, algorithm=self.get_hash_algorithm(self.flags) ) ctrk_hash_table += ctrk_hash return ctrk_hash_table def _calculate_rotkth(self) -> bytes: return internal_backend.hash( - self.ctrk_hash_table, f"sha{self.root_certs[0].pointQ.size_in_bits()}" + data=self.ctrk_hash_table, algorithm=self.get_hash_algorithm(self.flags) ) + def calculate(self) -> None: + """Calculate all internal members. + + :raises SPSDKError: The RoT certificates inputs are missing. + """ + # pylint: disable=invalid-name + if not self.root_certs_input: + raise SPSDKError("Root Key Record: The root of trust certificates are not specified.") + self.root_certs = [convert_to_ecc_key(cert) for cert in self.root_certs_input] + self.flags = self._calculate_flags() + self.ctrk_hash_table = self._create_ctrk_hash_table() + self.rotkth = self._calculate_rotkth() + self.root_public_key = self._create_root_public_key() + def export(self) -> bytes: """Export Root key record as bytes array.""" data = bytes() data += pack(" str: + """Get CTRK table hash algorithm. + + :param flags: Root Key Record flags + :return: Name of hash algorithm + """ + return {1: "sha256", 2: "sha384"}[flags & 0xF] + @classmethod - def parse(cls, data: bytes, offset: int = 0) -> "BaseClass": - """Parse Root key record from bytes array.This operation is not supported. + def parse(cls, data: bytes, offset: int = 0) -> "RootKeyRecord": + """Parse Root key record from bytes array. :param data: Input data as bytes array :param offset: The offset of input data - :raises NotImplementedError: This operation is not supported + :return: Root key record object """ - raise NotImplementedError("This operation is not supported.") + (flags,) = unpack_from("> 8 + number_of_hashes = (flags & 0xF0) >> 4 + rotkh_len = {0x1: 32, 0x2: 48}[flags & 0xF] + root_key_record = RootKeyRecord(ca_flag=ca_flag, root_certs=[], used_root_cert=used_rot_ix) + root_key_record.flags = flags + offset += 4 # move offset just after FLAGS + if number_of_hashes > 1: + root_key_record.ctrk_hash_table = data[offset : offset + rotkh_len * number_of_hashes] + offset += rotkh_len * number_of_hashes + root_key_record._calculate_rotkth() + root_key_record.root_public_key = data[offset : offset + rotkh_len * 2] + + return root_key_record class IskCertificate(BaseClass): @@ -647,43 +723,72 @@ class IskCertificate(BaseClass): def __init__( self, - constraints: int, - isk_private_key: Union[ECC.EccKey, bytes], - isk_cert: Union[ECC.EccKey, bytes], + constraints: int = 0, + signature_provider: Optional[SignatureProvider] = None, + isk_cert: Optional[Union[ECC.EccKey, bytes]] = None, user_data: Optional[bytes] = None, ) -> None: """Constructor for ISK certificate. :param constraints: Certificate version - :param isk_private_key: P-256 or P-384 ISK private key + :param signature_provider: ISK Signature Provider :param isk_cert: ISK certificate :param user_data: User data """ self.flags = 0 self.constraints = constraints - self.isk_private_key = convert_to_ecc_key(isk_private_key) - self.isk_cert = convert_to_ecc_key(isk_cert) + self.signature_provider = signature_provider + self.isk_cert = convert_to_ecc_key(isk_cert) if isk_cert else None self.user_data = user_data or bytes() self.signature = bytes() - self.coordinate_length = self.isk_private_key.pointQ.size_in_bytes() - self.isk_public_key_data = get_ecc_key_bytes(self.isk_cert) + self.coordinate_length = ( + self.signature_provider.signature_length // 2 if self.signature_provider else 0 + ) + self.isk_public_key_data = get_ecc_key_bytes(self.isk_cert) if self.isk_cert else bytes() self._calculate_flags() - self.signature_offset = calcsize("<3L") + len(self.user_data) - self.signature_offset += 2 * self.isk_cert.pointQ.size_in_bytes() - self.expected_size = ( + + @property + def signature_offset(self) -> int: + """Signature offset inside the ISK Certificate.""" + signature_offset = calcsize("<3L") + len(self.user_data) + if self.isk_cert: + signature_offset += 2 * self.isk_cert.pointQ.size_in_bytes() + + return signature_offset + + @property + def expected_size(self) -> int: + """Binary block expected size.""" + sign_len = len(self.signature) or ( + self.signature_provider.signature_length if self.signature_provider else 0 + ) + pub_key_len = ( + self.isk_cert.pointQ.size_in_bytes() * 2 + if self.isk_cert + else len(self.isk_public_key_data) + ) + + return ( 4 # signature offset + 4 # constraints + 4 # flags - + 2 * self.isk_cert.pointQ.size_in_bytes() # isk public key coordinates + + pub_key_len # isk public key coordinates + len(self.user_data) # user data - + 2 * self.isk_private_key.pointQ.size_in_bytes() # isk blob signature + + sign_len # isk blob signature ) def info(self) -> str: """Get info of ISK certificate.""" + isk_type = {0x1: "NIST P-256", 0x2: "NIST P-384"}[self.flags & 0xF] info = "" - info += f"Constraints: {self.constraints}\n" + info += f"Constraints: {self.constraints}\n" + if self.user_data: + info += f"User data: {self.user_data.hex()}\n" + else: + info += f"User data: Not included\n" + info += f"Type: {isk_type}\n" + info += f"Public Key: {str(self.isk_cert)}\n" return info def _calculate_flags(self) -> None: @@ -691,56 +796,81 @@ def _calculate_flags(self) -> None: self.flags = 0 if self.user_data: self.flags |= 1 << 31 + assert self.isk_cert if self.isk_cert.curve in ["NIST P-256", "p256"]: self.flags |= 1 << 0 if self.isk_cert.curve in ["NIST P-384", "p384"]: self.flags |= 1 << 1 - def create_isk_signature(self, key_record_data: bytes) -> None: - """Function to create ISK signature.""" + def create_isk_signature(self, key_record_data: bytes, force: bool = False) -> None: + """Function to create ISK signature. + + :raises SPSDKError: Signature provider is not specified. + """ # pylint: disable=invalid-name + if self.signature and not force: + return + if not self.signature_provider: + raise SPSDKError("ISK Certificate: The signature provider is not specified.") data = key_record_data + pack("<3L", self.signature_offset, self.constraints, self.flags) data += self.isk_public_key_data + self.user_data - self.signature = internal_backend.ecc_sign(self.isk_private_key, data) + self.signature = self.signature_provider.sign(data) def export(self) -> bytes: """Export ISK certificate as bytes array.""" if not self.signature: raise SPSDKError("Signature is not set.") - data = bytes() - data += pack("<3L", self.signature_offset, self.constraints, self.flags) - # data += pack("<2L", self.signature_offset) - # if self.isk_public_key: + data = pack("<3L", self.signature_offset, self.constraints, self.flags) data += self.isk_public_key_data if self.user_data: data += self.user_data data += self.signature + + assert len(data) == self.expected_size return data @classmethod - def parse(cls, data: bytes, offset: int = 0) -> "IskCertificate": + def parse( # type: ignore + cls, data: bytes, signature_size: int, offset: int = 0 + ) -> "IskCertificate": """Parse ISK certificate from bytes array.This operation is not supported. :param data: Input data as bytes array + :param signature_size: The signature size of ISK block :param offset: The offset of input data :raises NotImplementedError: This operation is not supported """ - raise NotImplementedError("This operation is not supported.") + (signature_offset, constraints, isk_flags) = unpack_from("<3L", data, offset) + signature_offset += offset + user_data_flag = bool(isk_flags & 0x80000000) + isk_pub_key_length = {0x1: 32, 0x2: 48}[isk_flags & 0xF] + offset += 3 * 4 + isk_pub_key_bytes = data[offset : offset + isk_pub_key_length * 2] + offset += isk_pub_key_length * 2 + user_data = data[offset:signature_offset] if user_data_flag else None + signature = data[signature_offset : signature_offset + signature_size] + + certificate = IskCertificate( + constraints=constraints, isk_cert=isk_pub_key_bytes, user_data=user_data + ) + certificate.signature = signature + return certificate class CertBlockV31(CertBlock): """Create Certificate block version 3.1.""" MAGIC = b"chdr" + FORMAT_VERSION = "2.1" def __init__( self, - root_certs: Union[Sequence[ECC.EccKey], Sequence[bytes]], - ca_flag: bool, + root_certs: Optional[Union[Sequence[ECC.EccKey], Sequence[bytes]]] = None, + ca_flag: bool = False, version: str = "2.1", used_root_cert: int = 0, constraints: int = 0, - isk_private_key: Optional[Union[ECC.EccKey, bytes]] = None, + signature_provider: Optional[SignatureProvider] = None, isk_cert: Optional[Union[ECC.EccKey, bytes]] = None, user_data: Optional[bytes] = None, ) -> None: @@ -751,36 +881,43 @@ def __init__( self.root_key_record = RootKeyRecord( ca_flag=ca_flag, used_root_cert=used_root_cert, root_certs=root_certs ) + self.isk_certificate = None - if not ca_flag: - if not isk_private_key: - raise SPSDKError("ISK private key is not set.") - if not isk_cert: - raise SPSDKError("ISK certificate is not set.") + if not ca_flag and signature_provider and isk_cert: self.isk_certificate = IskCertificate( constraints=constraints, - isk_private_key=isk_private_key, + signature_provider=signature_provider, isk_cert=isk_cert, user_data=user_data, ) - self.expected_size = self._calculate_expected_size() def _set_ca_flag(self, value: bool) -> None: self.root_key_record.ca_flag = value - def _calculate_expected_size(self) -> int: + def calculate(self) -> None: + """Calculate all internal members.""" + self.root_key_record.calculate() + + @property + def expected_size(self) -> int: + """Expected size of binary block.""" expected_size = self.header.SIZE expected_size += self.root_key_record.expected_size if self.isk_certificate: expected_size += self.isk_certificate.expected_size return expected_size + @property + def rkht(self) -> bytes: + """32-byte hash (SHA-256) of SHA-256 hashes of up to four root public keys.""" + return self.root_key_record.rotkth + def info(self) -> str: """Get info of Certificate block.""" msg = f"HEADER:\n{self.header.info()}\n" msg += f"ROOT KEY RECORD:\n{self.root_key_record.info()}\n" if self.isk_certificate: - msg += f"ISK\n{self.isk_certificate.info()}\n" + msg += f"ISK Certificate:\n{self.isk_certificate.info()}\n" return msg def export(self) -> bytes: @@ -794,17 +931,36 @@ def export(self) -> bytes: isk_cert_data = self.isk_certificate.export() self.header.cert_block_size += len(isk_cert_data) header_data = self.header.export() + if len(header_data + key_record_data + isk_cert_data) != self.expected_size: + raise SPSDKError("Ty vole spatna size!") return header_data + key_record_data + isk_cert_data @classmethod - def parse(cls, data: bytes, offset: int = 0) -> "BaseClass": + def parse(cls, data: bytes, offset: int = 0) -> "CertBlockV31": """Parse Certificate block from bytes array.This operation is not supported. :param data: Input data as bytes array :param offset: The offset of input data - :raises NotImplementedError: This operation is not supported + :raises SPSDKError: Magic do not match """ - raise NotImplementedError("This operation is not supported.") + # CertificateBlockHeader + cert_header = CertificateBlockHeader.parse(data, offset) + offset += len(cert_header) + # RootKeyRecord + root_key_record = RootKeyRecord.parse(data, offset) + offset += root_key_record.expected_size + # IskCertificate + isk_certificate = None + if root_key_record.ca_flag == 0: + isk_certificate = IskCertificate.parse( + data, len(root_key_record.root_public_key), offset + ) + # Certification Block V3.1 + cert_block = CertBlockV31() + cert_block.header = cert_header + cert_block.root_key_record = root_key_record + cert_block.isk_certificate = isk_certificate + return cert_block @classmethod def get_validation_schemas(cls) -> List[Dict[str, Any]]: @@ -826,16 +982,13 @@ def from_config( :return: Instance of CertBlockV3.1 :raises SPSDKError: If found gap in certificates from config file. """ - root_certificates_loaded: List[Optional[str]] = [ - config.get(f"rootCertificate{idx}File") for idx in range(4) - ] - # filter out None and empty values - root_certificates = list(filter(None, root_certificates_loaded)) - for org, filtered in zip(root_certificates_loaded, root_certificates): - if org != filtered: - raise SPSDKError("There are gaps in rootCertificateXFile definition") + binary_block = config.get("binaryCertificateBlock") + if binary_block: + return CertBlockV31.parse(misc.load_binary(binary_block, search_paths)) + + root_certificates = find_root_certificates(config) + main_root_cert_id = get_main_cert_index(config, search_paths=search_paths) - main_root_cert_id = get_main_cert_index(config, default=0) try: root_certificates[main_root_cert_id] except IndexError as e: @@ -844,6 +997,7 @@ def from_config( ) from e main_root_private_key_file = config.get("mainRootCertPrivateKeyFile") + signature_provider = config.get("iskSignProvider") use_isk = config.get("useIsk", False) isk_certificate = config.get("signingCertificateFile") isk_constraint = misc.value_to_int(config.get("signingCertificateConstraint", "0")) @@ -854,15 +1008,18 @@ def from_config( for cert_file in root_certificates ] user_data = None - isk_private_key = None + signature_provider = None isk_cert = None if use_isk: - assert isk_certificate and main_root_private_key_file + assert isk_certificate and (main_root_private_key_file or signature_provider) if isk_sign_data_path: user_data = misc.load_binary(isk_sign_data_path, search_paths=search_paths) - isk_private_key = misc.load_binary( - main_root_private_key_file, search_paths=search_paths + signature_provider = get_signature_provider( + signature_provider, + main_root_private_key_file, + search_paths=search_paths, + mode="deterministic-rfc6979", ) isk_cert = misc.load_binary(isk_certificate, search_paths=search_paths) @@ -873,8 +1030,9 @@ def from_config( constraints=isk_constraint, isk_cert=isk_cert, ca_flag=not use_isk, - isk_private_key=isk_private_key, + signature_provider=signature_provider, ) + cert_block.calculate() return cert_block @@ -885,17 +1043,91 @@ def validate(self) -> None: """ self.header.parse(self.header.export()) if self.isk_certificate: - if not isinstance(self.isk_certificate.isk_private_key, ECC.EccKey): + if not isinstance(self.isk_certificate.signature_provider, SignatureProvider): raise SPSDKError("Invalid ISK certificate.") - -def get_main_cert_index(config: Dict[str, Any], default: Optional[int] = None) -> int: + @staticmethod + def generate_config_template() -> str: + """Generate configuration for certification block v31.""" + val_schemas = CertBlockV31.get_validation_schemas() + val_schemas.append(ValidationSchemas.get_schema_file(CRYPTO_SCH_FILE)["cert_block_output"]) + yaml_data = CommentedConfig( + "Certification Block V31 template", + val_schemas, + ).export_to_yaml() + return yaml_data + + def create_config(self, data_path: str) -> str: + """Create configuration of the Certification block Image. + + :param data_path: Path to store the data files of configuration. + :return: Configuration dictionary. + """ + cfg: Dict[str, Union[str, int]] = {} + cfg["mainRootCertPrivateKeyFile"] = "N/A" + cfg["iskSignProvider"] = "N/A" + cfg["signingCertificatePrivateKeyFile"] = "N/A" + cfg["containerOutputFile"] = "N/A" + cfg["binaryCertificateBlock"] = "N/A" + for i in range(self.root_key_record.number_of_certificates): + key: Optional[ECC.EccKey] = None + if i == self.root_key_record.used_root_cert: + key = convert_to_ecc_key(self.root_key_record.root_public_key) + else: + if i < len(self.root_key_record.root_certs) and self.root_key_record.root_certs[i]: + key = convert_to_ecc_key(self.root_key_record.root_certs[i]) + if key: + key_file_name = os.path.join(data_path, f"rootCertificate{i}File.pub") + misc.write_file(key.export_key(format="PEM"), key_file_name) + cfg[f"rootCertificate{i}File"] = f"rootCertificate{i}File.pub" + else: + cfg[ + f"rootCertificate{i}File" + ] = "The public key is not possible reconstruct from the key hash" + + cfg["mainRootCertId"] = self.root_key_record.used_root_cert + if self.isk_certificate and self.root_key_record.ca_flag == 0: + cfg["useIsk"] = "true" + assert self.isk_certificate.isk_cert + key = self.isk_certificate.isk_cert + key_file_name = os.path.join(data_path, "signingCertificateFile.pub") + misc.write_file(key.export_key(format="PEM"), key_file_name) + cfg[f"signingCertificateFile"] = "signingCertificateFile.pub" + cfg["signingCertificateConstraint"] = self.isk_certificate.constraints + if self.isk_certificate.user_data: + key_file_name = os.path.join(data_path, "isk_user_data.bin") + misc.write_file(self.isk_certificate.user_data, key_file_name, mode="wb") + cfg["signCertData"] = "isk_user_data.bin" + else: + cfg["signCertData"] = "N/A" + else: + cfg["useIsk"] = "false" + cfg["signingCertificateFile"] = "N/A" + cfg["signingCertificateConstraint"] = "N/A" + cfg["signCertData"] = "N/A" + + val_schemas = CertBlockV31.get_validation_schemas() + val_schemas.append(ValidationSchemas.get_schema_file(CRYPTO_SCH_FILE)["cert_block_output"]) + + yaml_data = CommentedConfig( + main_title=( + "Certification block v3.1 recreated configuration from :" + f"{datetime.datetime.now().strftime('%d/%m/%Y %H:%M:%S')}." + ), + schemas=val_schemas, + values=cfg, + ).export_to_yaml() + return yaml_data + + +def get_main_cert_index(config: Dict[str, Any], search_paths: Optional[List[str]] = None) -> int: """Gets main certificate index from configuration. :param config: Input standard configuration. - :param default: List of paths where to search for the file, defaults to None + :param search_paths: List of paths where to search for the file, defaults to None :return: Instance of CertBlockV2 :raises SPSDKError: If invalid configuration is provided. + :raises SPSDKError: If correct certificate could not be identified. :raises SPSDKValueError: If certificate is not of correct type. """ root_cert_id = config.get("mainRootCertId") @@ -904,14 +1136,69 @@ def get_main_cert_index(config: Dict[str, Any], default: Optional[int] = None) - raise SPSDKError( "The mainRootCertId and mainRootCertId are specified and have different values." ) + found_cert_id = find_main_cert_index(config=config, search_paths=search_paths) if root_cert_id is None and cert_chain_id is None: - if default is None: - raise SPSDKError("Main cert ID is not specified. Use a property mainRootCertId.") - return default + if found_cert_id is not None: + return found_cert_id + else: + raise SPSDKError("Certificate could not be found") # root_cert_id may be 0 which is falsy value, therefore 'or' cannot be used - cert_index = root_cert_id if root_cert_id != None else cert_chain_id + cert_id = root_cert_id if root_cert_id is not None else cert_chain_id + try: + cert_id = int(cert_id) # type: ignore[arg-type] + except ValueError: + raise SPSDKValueError(f"A certificate index is not a number: {cert_id}") + if found_cert_id is not None and found_cert_id != cert_id: + logger.warning("Defined certificate does not match the private key.") + return cert_id + + +def find_main_cert_index( + config: Dict[str, Any], search_paths: Optional[List[str]] = None +) -> Optional[int]: + """Go through all certificates and find the index matching to private key. + + :param config: Configuration to be searched. + :param search_paths: List of paths where to search for the file, defaults to None + :return: List of root certificates. + """ + private_key_file = config.get("mainCertPrivateKeyFile") + if not private_key_file: + return None try: - cert_index = int(cert_index) # type: ignore[arg-type] + private_key_file_path = misc.find_file(private_key_file, search_paths=search_paths) + except SPSDKError: + logger.debug(f"A private key file {private_key_file} could not be found.") + return None + private_key = crypto.load_private_key(private_key_file_path) + root_certificates = find_root_certificates(config) + public_keys = [] + for root_crt_file in root_certificates: + try: + public_key = loaders.extract_public_key(root_crt_file, search_paths=search_paths) + public_keys.append(public_key) + except SPSDKError: + continue + try: + idx = get_matching_key_id(public_keys, private_key) + return idx except ValueError: - raise SPSDKValueError(f"A certificate index is not a number: {cert_index}") - return cert_index + return None + + +def find_root_certificates(config: Dict[str, Any]) -> List[str]: + """Find all root certificates in configuration. + + :param config: Configuration to be searched. + :raises SPSDKError: If invalid configuration is provided. + :return: List of root certificates. + """ + root_certificates_loaded: List[Optional[str]] = [ + config.get(f"rootCertificate{idx}File") for idx in range(4) + ] + # filter out None and empty values + root_certificates = list(filter(None, root_certificates_loaded)) + for org, filtered in zip(root_certificates_loaded, root_certificates): + if org != filtered: + raise SPSDKError("There are gaps in rootCertificateXFile definition") + return root_certificates diff --git a/spsdk/utils/crypto/certificate.py b/spsdk/utils/crypto/certificate.py index fed6faba..b372db80 100644 --- a/spsdk/utils/crypto/certificate.py +++ b/spsdk/utils/crypto/certificate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2022 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -11,6 +11,7 @@ from typing import Dict, Set from asn1crypto import x509 +from asn1crypto.keys import PublicKeyInfo from oscrypto.asymmetric import load_public_key, rsa_pkcs1v15_verify from oscrypto.errors import SignatureError @@ -64,18 +65,23 @@ def hash_algo(self) -> str: @property def public_key_modulus(self) -> int: """Modulus of the public key of the certificate.""" - return self._cert.public_key.native["public_key"]["modulus"] + return self.public_key.native["public_key"]["modulus"] @property def public_key_exponent(self) -> int: """Exponent of the public key of the certificate.""" - return self._cert.public_key.native["public_key"]["public_exponent"] + return self.public_key.native["public_key"]["public_exponent"] + + @property + def public_key(self) -> PublicKeyInfo: + """Public key.""" + return self._cert.public_key @property def public_key_hash(self) -> bytes: """32 bytes hash (SHA-256) of public key (modulus and exponent).""" - modulus = self._cert.public_key.native["public_key"]["modulus"] - exponent = self._cert.public_key.native["public_key"]["public_exponent"] + modulus = self.public_key.native["public_key"]["modulus"] + exponent = self.public_key.native["public_key"]["public_exponent"] modulus_len = (modulus.bit_length() + 7) // 8 exponent_len = (exponent.bit_length() + 7) // 8 return crypto_backend().hash( diff --git a/spsdk/utils/crypto/iee.py b/spsdk/utils/crypto/iee.py index 69d1ac99..2dea887b 100644 --- a/spsdk/utils/crypto/iee.py +++ b/spsdk/utils/crypto/iee.py @@ -148,6 +148,13 @@ def export(self) -> bytes: return pack(self._FORMAT, self.lock, self.key_attribute, self.aes_mode, 0) +class MasterId(Enum): + """Master IDs for RT118x.""" + + CM33 = 0b1000 # Cortex M33 Master ID + CM7 = 0b1001 # Cortex M7 Master ID + + class IeeKeyBlob: """IEE KeyBlob. @@ -196,6 +203,7 @@ def __init__( key2: Optional[bytes] = None, page_offset: int = 0, crc: Optional[bytes] = None, + master: Optional[MasterId] = None, ): """Constructor. @@ -219,6 +227,10 @@ def __init__( key1 = value_to_bytes(key1, byte_cnt=self.attributes.key1_size) key2 = value_to_bytes(key2, byte_cnt=self.attributes.key2_size) + if master == MasterId.CM33: + start_addr &= ~(1 << 28) + end_addr &= ~(1 << 28) + if start_addr < 0 or start_addr > end_addr or end_addr > 0xFFFFFFFF: raise SPSDKError("Invalid start/end address") @@ -227,6 +239,7 @@ def __init__( f"Start address must be aligned to {hex(self._START_ADDR_MASK + 1)} boundary" ) + self.master = master self.start_addr = start_addr self.end_addr = end_addr @@ -292,7 +305,7 @@ def encrypt_image_xts(self, base_address: int, data: bytes) -> bytes: key2 = reverse_bytes_in_longs(self.key2) for block in split_data(bytearray(data), self._IEE_ENCR_BLOCK_SIZE_XTS): - tweak = self.calculate_tweak(current_start) + tweak = self.calculate_tweak(current_start, self.master) encrypted_block = crypto_backend().aes_xts_encrypt( key1 + key2, @@ -315,10 +328,12 @@ def encrypt_image_ctr(self, base_address: int, data: bytes) -> bytes: key = reverse_bytes_in_longs(self.key1) nonce = reverse_bytes_in_longs(self.key2) + if self.master: + base_address |= self.master << 32 + counter = Counter(nonce, ctr_value=base_address >> 4, ctr_byteorder_encoding="big") for block in split_data(bytearray(data), self._ENCRYPTION_BLOCK_SIZE): - encrypted_block = crypto_backend().aes_ctr_encrypt( key, block, @@ -351,16 +366,19 @@ def encrypt_image(self, base_address: int, data: bytes) -> bytes: if self.attributes.ctr_mode: return self.encrypt_image_ctr(base_address, data) - else: - return self.encrypt_image_xts(base_address, data) + return self.encrypt_image_xts(base_address, data) @staticmethod - def calculate_tweak(address: int) -> bytes: + def calculate_tweak(address: int, master: Optional[MasterId] = None) -> bytes: """Calculate tweak value for AES-XTS encryption based on the address value. :param address: start address of encryption + :param master: MasterID :return: 16 byte tweak values """ + if master: + address |= master << 32 + sector = address >> 12 tweak = bytearray(16) for n in range(16): @@ -486,6 +504,7 @@ def __init__( self.database = Database(IEE_DATABASE_FILE) self.blobs_min_cnt = self.database.get_device_value("key_blob_min_cnt", device=family) self.blobs_max_cnt = self.database.get_device_value("key_blob_max_cnt", device=family) + self.generate_keyblob = self.database.get_device_value("generate_keyblob", device=family) if key_blobs: for key_blob in key_blobs: @@ -546,8 +565,8 @@ def get_blhost_script_otp_kek(self) -> str: ) fuses.load_registers_from_xml(xml_fuses, grouped_regs=grouped_regs) - fuses.find_reg(f"USER_KEY1").set_value(self.ibkek1) - fuses.find_reg(f"USER_KEY2").set_value(self.ibkek2) + fuses.find_reg("USER_KEY1").set_value(self.ibkek1) + fuses.find_reg("USER_KEY2").set_value(self.ibkek2) load_iee = fuses.find_reg("LOAD_IEE_KEY") load_iee.find_bitfield("LOAD_IEE_KEY_BITFIELD").set_value(1) @@ -623,16 +642,17 @@ def binary_image( :return: IEE in BinaryImage. """ iee = BinaryImage(image_name, offset=self.keyblob_address) - # Add mandatory IEE keyblob - iee_keyblobs = self.get_key_blobs() if plain_data else self.export_key_blobs() - iee.add_image( - BinaryImage( - keyblob_name, - offset=0, - description=f"IEE keyblobs {self.family}", - binary=iee_keyblobs, + if self.generate_keyblob: + # Add mandatory IEE keyblob + iee_keyblobs = self.get_key_blobs() if plain_data else self.export_key_blobs() + iee.add_image( + BinaryImage( + keyblob_name, + offset=0, + description=f"IEE keyblobs {self.family}", + binary=iee_keyblobs, + ) ) - ) binaries = self.export_image() if binaries: @@ -716,17 +736,31 @@ def load_from_config( :param search_paths: List of paths where to search for the file, defaults to None :return: initialized IEE object. """ - iee_config: List[Dict[str, Any]] = config["key_blobs"] + iee_config: List[Dict[str, Any]] = config.get("key_blobs", [config.get("key_blob")]) family = config["family"] - - ibkek1 = get_key(config["ibkek1"], 32) - ibkek2 = get_key(config["ibkek2"], 32) + master = config.get("master") + if master: + master = MasterId[master] + ibkek1 = get_key( + config.get( + "ibkek1", "0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" + ), + 32, + ) + ibkek2 = get_key( + config.get( + "ibkek2", "0x202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F" + ), + 32, + ) logger.debug(f"Loaded IBKEK1: {ibkek1.hex()}") logger.debug(f"Loaded IBKEK2: {ibkek2.hex()}") keyblob_address = value_to_int(config["keyblob_address"]) - start_address = min([value_to_int(addr["start_address"]) for addr in iee_config]) + start_address = min( + [value_to_int(addr.get("start_address", 0xFFFFFFFF)) for addr in iee_config] + ) data_blobs: Optional[List[Dict]] = config.get("data_blobs") binaries = None @@ -756,17 +790,11 @@ def load_from_config( binaries.add_image(binary) - iee = IeeNxp( - family, - keyblob_address, - ibkek1, - ibkek2, - binaries=binaries, - ) + iee = IeeNxp(family, keyblob_address, ibkek1, ibkek2, binaries=binaries) for key_blob_cfg in iee_config: aes_mode = key_blob_cfg["aes_mode"] - region_lock = "LOCK" if key_blob_cfg["region_lock"] else "UNLOCK" + region_lock = "LOCK" if key_blob_cfg.get("region_lock") else "UNLOCK" key_size = key_blob_cfg["key_size"] attributes = IeeKeyBlobAttribute( @@ -778,9 +806,9 @@ def load_from_config( key1 = get_key(key_blob_cfg["key1"], attributes.key1_size) key2 = get_key(key_blob_cfg["key2"], attributes.key2_size) - start_addr = value_to_int(key_blob_cfg["start_address"]) - end_addr = value_to_int(key_blob_cfg["end_address"]) - page_offset = value_to_int(key_blob_cfg["page_offset"]) + start_addr = value_to_int(key_blob_cfg.get("start_address", start_address)) + end_addr = value_to_int(key_blob_cfg.get("end_address", 0xFFFFFFFF)) + page_offset = value_to_int(key_blob_cfg.get("page_offset", 0)) iee.add_key_blob( IeeKeyBlob( @@ -790,6 +818,7 @@ def load_from_config( key1=key1, key2=key2, page_offset=page_offset, + master=master, ) ) diff --git a/spsdk/utils/crypto/otfad.py b/spsdk/utils/crypto/otfad.py index 4898305a..3dfc7fba 100644 --- a/spsdk/utils/crypto/otfad.py +++ b/spsdk/utils/crypto/otfad.py @@ -21,6 +21,7 @@ from spsdk.apps.utils.utils import get_key from spsdk.exceptions import SPSDKValueError from spsdk.utils.database import Database +from spsdk.utils.exceptions import SPSDKRegsErrorBitfieldNotFound from spsdk.utils.images import BinaryImage from spsdk.utils.misc import ( align_block, @@ -584,25 +585,41 @@ def get_blhost_script_otp_kek(self, index: int = 1) -> str: xml_fuses = os.path.join(OTFAD_DATA_FOLDER, xml_fuses) fuses = Registers(self.family, base_endianness="little") + self.database.index = index + grouped_regs = self.database.get_device_value( "grouped_registers", device=self.family, default=None ) - if grouped_regs: - for reg in grouped_regs: - reg["name"] = reg["name"].replace("{index}", str(index)) + fuses.load_registers_from_xml(xml_fuses, filter_out_list, grouped_regs) scramble_enabled = ( self.key_scramble_mask is not None and self.key_scramble_align is not None ) - fuses.find_reg(f"OTFAD{index}_KEY").set_value(self.kek) - otfad_cfg = fuses.find_reg(f"OTFAD{index}_CFG4") - otfad_cfg.find_bitfield("OTFAD_ENABLE").set_value(1) + otfad_key_fuse = self.database.get_device_value("otfad_key_fuse", self.family) + otfad_cfg_fuse = self.database.get_device_value("otfad_cfg_fuse", self.family) + + fuses.find_reg(otfad_key_fuse).set_value(self.kek) + otfad_cfg = fuses.find_reg(otfad_cfg_fuse) + + try: + otfad_cfg.find_bitfield( + self.database.get_device_value("otfad_enable_bitfield", self.family) + ).set_value(1) + except SPSDKRegsErrorBitfieldNotFound as e: + logger.debug(f"Bitfield for OTFAD ENABLE not found for {self.family}") + if scramble_enabled: - otfad_cfg.find_bitfield("OTFAD_SCRAMBLE_ENABLE").set_value(1) - otfad_cfg.find_bitfield("OTFAD_SCRAMBLE_ALIGN").set_value(self.key_scramble_align) - fuses.find_reg(f"OTFAD{index}_KEY_SCRAMBLE").set_value(self.key_scramble_mask) + otfad_cfg.find_bitfield( + self.database.get_device_value("otfad_scramble_enable_bitfield", self.family) + ).set_value(1) + otfad_cfg.find_bitfield( + self.database.get_device_value("otfad_scramble_align_bitfield", self.family) + ).set_value(self.key_scramble_align) + fuses.find_reg( + self.database.get_device_value("otfad_scramble_key", self.family) + ).set_value(self.key_scramble_mask) ret = ( f"# BLHOST OTFAD{index} KEK fuses programming script\n" @@ -611,7 +628,7 @@ def get_blhost_script_otp_kek(self, index: int = 1) -> str: ) ret += f"# OTP KEK (Big Endian): {self.kek.hex()}\n\n" - for reg in fuses.find_reg(f"OTFAD{index}_KEY").sub_regs: + for reg in fuses.find_reg(otfad_key_fuse).sub_regs: ret += f"# {reg.name} fuse.\n" ret += f"efuse-program-once {reg.offset} 0x{reg.get_bytes_value().hex()} --no-verify\n" @@ -621,7 +638,9 @@ def get_blhost_script_otp_kek(self, index: int = 1) -> str: ret += f"efuse-program-once {otfad_cfg.offset} 0x{otfad_cfg.get_bytes_value().hex()} --no-verify\n" if scramble_enabled: - scramble = fuses.find_reg(f"OTFAD{index}_KEY_SCRAMBLE") + scramble = fuses.find_reg( + self.database.get_device_value("otfad_scramble_key", self.family) + ) ret += f"\n# {scramble.name} fuse.\n" ret += f"efuse-program-once {scramble.offset} 0x{scramble.get_bytes_value().hex()} --no-verify\n" @@ -675,11 +694,17 @@ def export_image( return binaries - def binary_image(self, plain_data: bool = False, data_alignment: int = 16) -> BinaryImage: + def binary_image( + self, + plain_data: bool = False, + data_alignment: int = 16, + otfad_table_name: str = "OTFAD_Table", + ) -> BinaryImage: """Get the OTFAD Binary Image representation. :param plain_data: Binary representation in plain format, defaults to False :param data_alignment: Alignment of data part key blobs. + :param otfad_table_name: name of the output file that contains OTFAD table :return: OTFAD in BinaryImage. """ otfad = BinaryImage("OTFAD", offset=self.table_address) @@ -696,7 +721,7 @@ def binary_image(self, plain_data: bool = False, data_alignment: int = 16) -> Bi ) otfad.add_image( BinaryImage( - "OTFAD_Table", + otfad_table_name, size=self.key_blob_rec_size * self.blobs_max_cnt, offset=0, description=f"OTFAD description table for {self.family}", @@ -789,7 +814,7 @@ def load_from_config( otfad_config: List[Dict[str, Any]] = config["key_blobs"] family = config["family"] database = Database(OTFAD_DATABASE_FILE) - kek = get_key(find_file(config["kek"], search_paths=search_paths), 16) + kek = get_key(config["kek"], expected_size=16, search_paths=search_paths) logger.debug(f"Loaded KEK: {kek.hex()}") table_address = value_to_int(config["otfad_table_address"]) start_address = min([value_to_int(addr["start_address"]) for addr in otfad_config]) diff --git a/spsdk/utils/database.py b/spsdk/utils/database.py index 26f45635..8ede80f2 100644 --- a/spsdk/utils/database.py +++ b/spsdk/utils/database.py @@ -11,7 +11,7 @@ from dataclasses import dataclass from typing import Any, Dict, List, Optional -from spsdk.exceptions import SPSDKTypeError, SPSDKValueError +from spsdk.exceptions import SPSDKError, SPSDKTypeError, SPSDKValueError from spsdk.utils.misc import find_first, load_configuration @@ -137,7 +137,9 @@ class Devices(List[Device]): @property def device_names(self) -> List[str]: """Get the list of all device names.""" - return [dev.name for dev in self] + devices = [dev.name for dev in self] + devices.sort() + return devices def get_by_name(self, name: str) -> Device: """Return database device structure. @@ -176,19 +178,21 @@ def load_from_file(path: str) -> "Devices": class Database: """Class that helps manage used databases in SPSDK.""" - def __init__(self, path: str) -> None: + def __init__(self, path: str, index: Optional[int] = None) -> None: """Register Configuration class constructor. :param path: The path to configuration JSON file. + :param index: Values with {index} will be replaced with index value """ self.path = path + self.index = index config: Dict[str, Any] = load_configuration(path) try: self._devices = Devices.load(config["devices"]) except (SPSDKValueError, SPSDKTypeError) as exc: if exc.description: exc.description += f"File path: {self.path}" - raise exc + raise SPSDKError("Database can not be created") self.attributes: dict = config.get("attributes", {}) @classmethod @@ -205,6 +209,19 @@ def devices(self) -> Devices: """Get the list of devices stored in the database.""" return self._devices + def replace_idx_value(self, value: str) -> str: + """Replace index value if provided in the database. + + :param value: value to be replaced f-string containing index + :return: value with replaced index + """ + if self.index and isinstance(value, str): + value = value.replace("{index}", str(self.index)) + if self.index and isinstance(value, list): + for reg in value: + reg["name"] = reg["name"].replace("{index}", str(self.index)) + return value + def get_device_value( self, key: str, @@ -212,7 +229,7 @@ def get_device_value( revision: str = "latest", default: Optional[Any] = None, ) -> Any: - """Return any parameter by key. + """Return any parameter by key and replace the index if provided in DB. :param key: The Key of the parameter to be returned. :param device: The device name. @@ -227,7 +244,8 @@ def get_device_value( except SPSDKValueError: rev = None if rev and key in rev.attributes: - return rev.attributes[key] + return self.replace_idx_value(rev.attributes[key]) if key in dev.attributes: - return dev.attributes[key] - return self.attributes.get(key, default) + return self.replace_idx_value(dev.attributes[key]) + + return self.replace_idx_value(self.attributes.get(key, default)) diff --git a/spsdk/utils/easy_enum.py b/spsdk/utils/easy_enum.py index ab45268a..c46ebd0b 100644 --- a/spsdk/utils/easy_enum.py +++ b/spsdk/utils/easy_enum.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# # Copyright 2019 Martin Olejar # Copyright 2020-2023 NXP # @@ -26,6 +29,7 @@ from typing import Optional, Sequence, Union from spsdk import SPSDKError +from spsdk.exceptions import SPSDKKeyError, SPSDKTypeError EnumKeyType = Union[str, int] @@ -63,15 +67,15 @@ def __getitem__(cls, key): for name, value, _ in cls._items_: if key.upper() == name.upper(): return value - raise KeyError(f"'{cls.__name__}' has no item with name '{key}'") + raise SPSDKKeyError(f"'{cls.__name__}' has no item with name '{key}'") if isinstance(key, int): for name, value, _ in cls._items_: if key == value: return name - raise KeyError(f"'{cls.__name__}' has no item with value '{key}'") + raise SPSDKKeyError(f"'{cls.__name__}' has no item with value '{key}'") - raise TypeError(f"'{cls.__name__}' has no item with type '{type(key)}'") + raise SPSDKTypeError(f"'{cls.__name__}' has no item with type '{type(key)}'") def __iter__(cls): return (item for item in cls._items_) @@ -110,7 +114,7 @@ def desc(cls, key: EnumKeyType, default: str = "") -> str: :param key: either value or name (name is case INSENSITIVE) :param default: value in case key does not exist :return: description of the value; empty string if description was not specified - :raises TypeError: Key is nor string or int + :raises SPSDKTypeError: Key is nor string or int """ # pylint: disable=no-member if isinstance(key, str): @@ -125,7 +129,7 @@ def desc(cls, key: EnumKeyType, default: str = "") -> str: return desc return default - raise TypeError(f"'{cls.__name__}' has no item with type '{type(key)}'") + raise SPSDKTypeError(f"'{cls.__name__}' has no item with type '{type(key)}'") @classmethod def name(cls, key: int, default: Optional[str] = None) -> str: @@ -134,7 +138,7 @@ def name(cls, key: int, default: Optional[str] = None) -> str: :param key: enumeration tag :param default: value to return of tag not found; if not defined, KeyError exception will be raised :return: name of the corresponding enumeration tag - :raise KeyError: if tag not supported and default value not provided + :raise SPSDKKeyError: if tag not supported and default value not provided """ # pylint: disable=no-member for name, value, _ in cls._items_: @@ -142,7 +146,7 @@ def name(cls, key: int, default: Optional[str] = None) -> str: return name if default is None: - raise KeyError("Enumeration not supported: " + str(key)) + raise SPSDKKeyError("Enumeration not supported: " + str(key)) return default diff --git a/spsdk/utils/exceptions.py b/spsdk/utils/exceptions.py index 6b49e590..994ef9c5 100644 --- a/spsdk/utils/exceptions.py +++ b/spsdk/utils/exceptions.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -29,5 +29,5 @@ class SPSDKRegsErrorEnumNotFound(SPSDKRegsError): """Enum has not been found.""" -class SPSDKTimeoutError(SPSDKError): +class SPSDKTimeoutError(TimeoutError, SPSDKError): """SPSDK Timeout.""" diff --git a/spsdk/utils/images.py b/spsdk/utils/images.py index c3a14a1f..56d0cf31 100644 --- a/spsdk/utils/images.py +++ b/spsdk/utils/images.py @@ -32,7 +32,7 @@ # bincopy will be loaded lazily as needed, this is just to satisfy type-hint checkers import bincopy -BINARY_SCH_FILE = os.path.join(SPSDK_DATA_FOLDER, "image", "sch_binary.yml") +BINARY_SCH_FILE = os.path.join(SPSDK_DATA_FOLDER, "image", "sch_binary.yaml") logger = logging.getLogger(__name__) @@ -204,7 +204,7 @@ def validate(self) -> None: begin = image.offset end = begin + len(image) - 1 # Check if it fits inside the parent image - if end > len(self): + if end >= len(self): raise SPSDKOverlapError( f"The image {image.name} doesn't fit into {self.name} parent image." ) @@ -231,7 +231,7 @@ def get_min_draw_width(self, include_sub_images: bool = True) -> int: """ widths = [ self.MINIMAL_DRAW_WIDTH, - len(f"+--0x0000_0000--{self.name}--+"), + len(f"+==-0x0000_0000= {self.name} =+"), len(f"|Size: {size_fmt(len(self), False)}|"), ] if include_sub_images: @@ -246,6 +246,7 @@ def draw( color: str = "", no_color: bool = False, ) -> str: + # fmt: off """Draw the image into the ASCII graphics. :param include_sub_images: Include also sub images into, defaults to True @@ -255,22 +256,23 @@ def draw( :raises SPSDKValueError: In case of invalid width. :return: ASCII art representation of image. """ - # +--0x0000_0000--Title1---------------+ + # +==0x0000_0000==Title1===============+ # | Size: 2048B | # | Description1 | # | Description1 2nd line | - # |+--0x0000_0000--Title11------------+| + # |+==0x0000_0000==Title11============+| # || Size: 512B || # || Description11 || # || Description11 2nd line || - # |+--0x0000_01FF---------------------+| + # |+==0x0000_01FF=====================+| # | | - # |+--0x0000_0210--Title12------------+| + # |+==0x0000_0210==Title12============+| # || Size: 512B || # || Description12 || # || Description12 2nd line || - # |+--0x0000_041F---------------------+| - # +--0x0000_07FF-----------------------+ + # |+==0x0000_041F=====================+| + # +==0x0000_07FF=======================+ + # fmt: on def _get_centered_line(text: str) -> str: text_len = len(text) spaces = width - text_len - 2 @@ -307,8 +309,8 @@ def wrap_block(inner: str) -> str: ) # - Title line - header = f"+--{format_value(self.absolute_address, 32)}--{self.name}--" - block += color + f"{header}{'-'*(width-len(header)-1)}+\n" + header = f"+=={format_value(self.absolute_address, 32)}= {self.name} =" + block += color + f"{header}{'='*(width-len(header)-1)}+\n" # - Size block += _get_centered_line(f"Size: {size_fmt(len(self), False)}") # - Description @@ -337,8 +339,8 @@ def wrap_block(inner: str) -> str: block += wrap_block(inner_block) # - Closing line - footer = f"+--{format_value(self.absolute_address + len(self) - 1, 32)}--" - block += color + f"{footer}{'-'*(width-len(footer)-1)}+\n" + footer = f"+=={format_value(self.absolute_address + len(self) - 1, 32)}==" + block += color + f"{footer}{'='*(width-len(footer)-1)}+\n" if self.parent is None: block += "\n" + "" if no_color else colorama.Fore.RESET @@ -408,10 +410,17 @@ def load_from_config( for i, region in enumerate(regions): binary_file: Dict = region.get("binary_file") if binary_file: - binary = load_binary(binary_file["path"], search_paths=search_paths) offset = binary_file["offset"] name = binary_file.get("name", binary_file["path"]) - ret.add_image(BinaryImage(name, len(binary), offset, binary=binary)) + ret.add_image( + BinaryImage.load_binary_image( + binary_file["path"], + name=name, + offset=offset, + pattern=pattern, + search_paths=search_paths, + ) + ) binary_block: Dict = region.get("binary_block") if binary_block: size = binary_block["size"] diff --git a/spsdk/utils/misc.py b/spsdk/utils/misc.py index 6839e327..de55f404 100644 --- a/spsdk/utils/misc.py +++ b/spsdk/utils/misc.py @@ -11,6 +11,7 @@ import math import os import re +import sys import time from math import ceil from struct import pack, unpack @@ -116,7 +117,7 @@ def align(number: int, alignment: int = 4) -> int: def align_block( data: Union[bytes, bytearray], alignment: int = 4, - padding: Optional[Union[int, BinaryPattern]] = None, + padding: Optional[Union[int, str, BinaryPattern]] = None, ) -> bytes: """Align binary data block length to specified boundary by adding padding bytes to the end. @@ -401,10 +402,11 @@ def format_value(value: int, size: int, delimiter: str = "_", use_prefix: bool = """ 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]) + sign = "-" if value < 0 else "" + parts = re.findall(".{1,4}", f"{abs(value):0{padding}{infix}}"[::-1]) rev = delimiter.join(parts)[::-1] prefix = f"0{infix}" if use_prefix else "" - return f"{prefix}{rev}" + return f"{sign}{prefix}{rev}" def get_bytes_cnt_of_int( @@ -825,3 +827,39 @@ def get_hash(text: Union[str, bytes]) -> str: if isinstance(text, str): text = text.encode("utf-8") return hashlib.sha1(text).digest().hex()[:8] + + +def import_source(source: str) -> None: + """Import Python source file directly. + + :param source: A path to python source file or existing module name + Accepted values are: + - an absolute path to py file + - a path to py file, relative to current working directory + - installed package/module name + and more, and more + :raises SPSDKError: If importing of source file failed + """ + from importlib.util import ( # pylint: disable=import-outside-toplevel + find_spec, + module_from_spec, + spec_from_file_location, + ) + + if not os.path.isfile(source): + spec = find_spec(name=source) + else: + module_name = os.path.splitext(os.path.basename(source))[0] + spec = spec_from_file_location(name=module_name, location=source) # type: ignore + if not spec: + raise SPSDKError( + f"Source '{source}' does not exist.Check if it is valid file path/module name" + ) + + mod = module_from_spec(spec) + try: + sys.modules[spec.name] = mod + spec.loader.exec_module(mod) # type: ignore + logger.debug(f"A module {source} has been imported.") + except Exception as e: + raise SPSDKError(f"Failed to load source {source}: {e}") from e diff --git a/spsdk/utils/registers.py b/spsdk/utils/registers.py index dbe6684b..93ebfd6a 100644 --- a/spsdk/utils/registers.py +++ b/spsdk/utils/registers.py @@ -374,7 +374,7 @@ def set_enum_value(self, new_val: str, raw: bool = False) -> None: try: val_int = value_to_int(new_val) except TypeError: - raise exc # pylint: disable=raise-missing-from + raise SPSDKRegsErrorEnumNotFound # pylint: disable=raise-missing-from self.set_value(val_int, raw) def get_enum_value(self) -> Union[str, int]: diff --git a/spsdk/utils/schema_validator.py b/spsdk/utils/schema_validator.py index cbaf742d..90ebb141 100644 --- a/spsdk/utils/schema_validator.py +++ b/spsdk/utils/schema_validator.py @@ -11,6 +11,7 @@ import logging import os import textwrap +from collections import OrderedDict from typing import Any, Callable, Dict, List, Optional, Tuple, Union import fastjsonschema @@ -19,11 +20,11 @@ from deepmerge.strategy.list import ListStrategies from deepmerge.strategy.set import SetStrategies from ruamel.yaml import YAML, YAMLError -from ruamel.yaml.comments import CommentedMap as CM -from ruamel.yaml.comments import CommentedSeq as CS +from ruamel.yaml.comments import CommentedMap as CMap +from ruamel.yaml.comments import CommentedSeq as CSeq from spsdk import SPSDK_YML_INDENT, SPSDKError -from spsdk.utils.misc import find_file, load_configuration, value_to_int +from spsdk.utils.misc import find_file, load_configuration, value_to_int, write_file ENABLE_DEBUG = False @@ -35,17 +36,20 @@ class SPSDKListStrategies(ListStrategies): # pylint: disable=unused-argument # because of the base class @staticmethod - def strategy_set(config, path, base, nxt): # type: ignore + def strategy_set(_config, _path, base, nxt): # type: ignore """Use the set of both as a output.""" try: ret = list(set(base + nxt)) ret.sort() except TypeError: - logger.warning( - "Found unhashable object in List 'set' strategy during merge." - " It was used 'override' method instead of 'set'." - ) - ret = nxt + try: + ret = base + nxt + except TypeError: + logger.warning( + "Found unhashable object in List 'set' strategy during merge." + " It was used 'override' method instead of 'set'." + ) + ret = nxt return ret @@ -83,6 +87,27 @@ def _print_validation_fail_reason( :param extra_formaters: Additional custom formatters :return: String explaining the reason of fail. """ + + def process_nested_rule( + exception: fastjsonschema.JsonSchemaValueException, + extra_formaters: Optional[Dict[str, Callable[[str], bool]]], + ) -> str: + message = "" + for rule_def_ix, rule_def in enumerate(exception.rule_definition): + try: + validator = fastjsonschema.compile(rule_def, formats=extra_formaters) + validator(exception.value) + except fastjsonschema.JsonSchemaValueException as _exc: + message += ( + f"\nReason of fail for {exception.rule} rule#{rule_def_ix}: " + f"\n {_print_validation_fail_reason(_exc , extra_formaters)}\n" + ) + if all(rule_def.get("required") for rule_def in exception.rule_definition): + message += f"\nYou need to define {exception.rule} of the following sets:" + for rule_def in exc.rule_definition: + message += f" {rule_def['required']}" + return message + message = str(exc) if exc.rule == "required": missing = filter(lambda x: x not in exc.value.keys(), exc.rule_definition) @@ -91,25 +116,9 @@ def _print_validation_fail_reason( if exc.rule_definition == "file": message += f"; Non-existing file: {exc.value}" elif exc.rule == "anyOf": - message += "\nYou need to define at least one of the following sets:" - for rule_def in exc.rule_definition: - message += f" {rule_def['required']}" + message += process_nested_rule(exc, extra_formaters=extra_formaters) elif exc.rule == "oneOf": - # re-run just the part where the rule failed and get the exact mistake to print - for rule_def_ix, rule_def in enumerate(exc.rule_definition): - try: - oneof_validator = fastjsonschema.compile(rule_def, formats=extra_formaters) - oneof_validator(exc.value) - except fastjsonschema.JsonSchemaValueException as oneof_exc: - message += ( - f"\nReason of fail for OneOf rule#{rule_def_ix}: " - f"\n {_print_validation_fail_reason(oneof_exc, extra_formaters)}\n" - ) - # check if the error is caused by "required oneOf" condition - if all(rule_def.get("required") for rule_def in exc.rule_definition): - message += "\nYou need to define exactly one of the following sets:" - for rule_def in exc.rule_definition: - message += f" {rule_def['required']}" + message += process_nested_rule(exc, extra_formaters=extra_formaters) return message @@ -148,12 +157,12 @@ def check_config( schema: Dict[str, Any] = {} for sch in schemas: always_merger.merge(schema, copy.deepcopy(sch)) + validator = None formats = always_merger.merge(custom_formatters, extra_formaters or {}) try: if ENABLE_DEBUG: validator_code = fastjsonschema.compile_to_code(schema, formats=formats) - with open("validator_file.py", "w") as f: - f.write(validator_code) + write_file(validator_code, "validator_file.py") else: validator = fastjsonschema.compile(schema, formats=formats) except (TypeError, fastjsonschema.JsonSchemaDefinitionException) as exc: @@ -165,34 +174,41 @@ def check_config( validator_file.validate(config_to_check, formats) else: + assert validator is not None validator(config_to_check) except fastjsonschema.JsonSchemaValueException as exc: message = _print_validation_fail_reason(exc, formats) raise SPSDKError(f"Configuration validation failed: {message}") from exc -class ConfigTemplate: - """Class for generating commented config templates.""" +class CommentedConfig: + """Class for generating commented config templates or custom configurations.""" def __init__( self, main_title: str, schemas: List[Dict[str, Any]], - override_values: Optional[Dict[str, Any]] = None, + values: Optional[Dict[str, Any]] = None, note: Optional[str] = None, + export_template: bool = True, ): """Constructor for Config templates. :param main_title: Main title of final template. :param schemas: Main description of final template. - :param override_values: Additional overriding default values. + :param values: + - for configuration, this is dictionary of values to be saved + - for schema, this is dictionary of override values (overriding default values) :param note: Additional Note after title test. + :param export_template: True to export schema template; False to export custom configuration """ self.main_title = main_title self.schemas = schemas - self.override_values = override_values + self.values = values self.indent = 0 self.note = note + self.export_template = export_template + assert export_template or values, "values must be defined for configuration export" @staticmethod def _get_title_block(title: str, description: Optional[str] = None) -> str: @@ -219,14 +235,14 @@ def _get_title_block(title: str, description: Optional[str] = None) -> str: return ret @staticmethod - def _get_required(key: str, block: Dict[str, Any]) -> str: - """Function to determine if the config key is required or not. + def get_property_optional_required(key: str, block: Dict[str, Any]) -> str: + """Function to determine if the config property is required or not. :param key: Name of config record :param block: Source data block :return: Final description. """ - schema_kws = ["allOf", "anyOf", "oneOf"] + schema_kws = ["allOf", "anyOf", "oneOf", "if", "then", "else"] def _find_required(d_in: Dict[str, Any]) -> Optional[List[str]]: if "required" in d_in: @@ -239,17 +255,21 @@ def _find_required(d_in: Dict[str, Any]) -> Optional[List[str]]: return ret return None - def _find_required_in_schema_kws(d_in: Dict[str, Any]) -> List[str]: - """Find all required properties in structure composed of nested properties such as allOf/anyOf/oneOf.""" + def _find_required_in_schema_kws(schema_node: Union[List, Dict[str, Any]]) -> List[str]: + """Find all required properties in structure composed of nested properties.""" all_props: List[str] = [] - for k, v in d_in.items(): - if k == "required": - all_props.extend(v) - elif k in schema_kws: - for item in v: - req_props = _find_required_in_schema_kws(item) + if isinstance(schema_node, dict): + for k, v in schema_node.items(): + if k == "required": + all_props.extend(v) + elif k in schema_kws: + req_props = _find_required_in_schema_kws(v) all_props.extend(req_props) - return all_props + if isinstance(schema_node, list): + for item in schema_node: + req_props = _find_required_in_schema_kws(item) + all_props.extend(req_props) + return list(set(all_props)) if "required" in block and key in block["required"]: return "Required" @@ -270,30 +290,39 @@ def _find_required_in_schema_kws(d_in: Dict[str, Any]) -> List[str]: def _create_object_block( self, block: Dict[str, Dict[str, Any]], - order_list: Optional[List[str]] = None, - ) -> CM: + custom_value: Optional[Dict[str, Any]] = None, + ) -> CMap: """Private function used to create object block with data. :param block: Source block with data - :param order_list: Optional list with right order of records. + :param custom_value: + Optional dictionary of properties to be exported. + It is recommeded to pass OrderedDict to preserve the key order. + - key is property ID to be exported + - value is its value; or None if default value shall be used :return: CM base configuration object :raises SPSDKError: In case of invalid data pattern. """ assert block.get("type") == "object" self.indent += 1 - cfg_m = CM() + cfg_m = CMap() assert "properties" in block.keys() - key_order = order_list or list(block["properties"].keys()) + key_order = ( + self._get_schema_block_keys(block) + if (custom_value is None) + else list(custom_value.keys()) + ) for key in key_order: assert key in block["properties"].keys() val_p: Dict = block["properties"][key] - value_to_add, comment, title = self._get_schema_value(val_p) - - if self.override_values and key in self.override_values: - value_to_add = self.override_values[key] + if (custom_value is not None) and (custom_value[key] is not None): + value_to_add = custom_value[key] + else: + value_to_add = None + value_to_add, comment, title = self._get_schema_value(val_p, value_to_add) cfg_m[key] = value_to_add - p_required = self._get_required(key, block) + p_required = self.get_property_optional_required(key, block) cfg_m.yaml_add_eol_comment(f"[{p_required}]{', ' if comment else ''}{comment}", key=key) if title: cfg_m.yaml_set_comment_before_after_key( @@ -304,97 +333,130 @@ def _create_object_block( self.indent -= 1 return cfg_m - def _create_array_block(self, block: Dict[str, Dict[str, Any]]) -> CS: + def _create_array_block( + self, block: Dict[str, Dict[str, Any]], custom_value: Optional[List[Any]] + ) -> CSeq: """Private function used to create array block with data. :param block: Source block with data :return: CS base configuration object :raises SPSDKError: In case of invalid data pattern. """ - self.indent += 1 assert block.get("type") == "array" - cfg_s = CS() assert "items" in block.keys() + self.indent += 1 val_i: Dict = block["items"] - value, comment, title = self._get_schema_value(val_i) - if isinstance(value, CS): - self.indent -= 1 - return value - if isinstance(value, list): - cfg_s.extend(value) + if "oneOf" in val_i.keys(): + cfg_s = self._create_one_of_block_array(val_i, custom_value) else: - cfg_s.append(value) - if comment: - cfg_s.yaml_add_eol_comment(comment, key=0) - if title: - cfg_s.yaml_set_comment_before_after_key( - 0, - self._get_title_block(title), - indent=SPSDK_YML_INDENT * (self.indent - 1), - ) + cfg_s = CSeq() + if custom_value: + for cust_val in custom_value: + value, comment, title = self._get_schema_value(val_i, cust_val) + cfg_s.append(value) + else: + value, comment, title = self._get_schema_value(val_i, None) + # the template_value can be the actual list(not only one element) + if isinstance(value, list): + cfg_s.extend(value) + else: + cfg_s.append(value) self.indent -= 1 return cfg_s - def _create_one_of_block(self, block: Dict[str, Dict[str, Any]]) -> CS: - """Private function used to create oneOf block with data. + @staticmethod + def _find_matching_oneof_option(one_of: List[Dict[str, Any]], cust_val: Any) -> Dict[str, Any]: + """Find matching "oneOf" schema for given custom value. + + :param one_of: list of oneOf schemas + :param cust_val: custom value; currently must be dictionary + :raise SPSDKError: if not found + """ + assert ( + isinstance(cust_val, dict) and cust_val + ), "currently the implementation supports only dictionary as custom value" + for option in one_of: + assert option.get("type") == "object" + properties = option.get("properties") + assert properties, "non-empty properties must be defined" + if all([key in properties for key in cust_val.keys()]): + return option + raise SPSDKError(f"for custom value {str(cust_val)}, no corresponding `oneOf` schema found") + + def _create_one_of_block_array( + self, block: Dict[str, Dict[str, Any]], custom_value: Optional[List[Any]] + ) -> CSeq: + """Private function used to create oneOf block with data, and return as an array that contaisn all values. :param block: Source block with data + :param custom_value: custom value to fill the array :return: CS base configuration object """ self.indent += 1 - ret = CS() + ret = CSeq() one_of = block["oneOf"] assert isinstance(one_of, list) - option_types = ",".join([str(x.get("type")) for x in one_of]) - title = f"List of possible {len(one_of)} options. Option types[{option_types}]" - for i, option in enumerate(one_of): - value, loc_comment, loc_title = self._get_schema_value(option) - ret.append(value) - ret.yaml_add_eol_comment( - f"[Example of possible configuration #{i}] {loc_comment}", key=i - ) - if loc_title: - ret.yaml_set_comment_before_after_key( - key=i, - before=self._get_title_block(loc_title), - indent=SPSDK_YML_INDENT * (self.indent - 1), + if custom_value is not None: + for i, cust_val in enumerate(custom_value): + option = self._find_matching_oneof_option(one_of, cust_val) + value, loc_comment, loc_title = self._get_schema_value(option, cust_val) + ret.append(value) + if loc_comment: + ret.yaml_add_eol_comment(f"{loc_comment}", key=i) + if loc_title: + ret.yaml_set_comment_before_after_key( + key=i, + before=self._get_title_block(loc_title), + indent=SPSDK_YML_INDENT * (self.indent - 1), + ) + else: + option_types = ",".join([str(x.get("type")) for x in one_of]) + title = f"List of possible {len(one_of)} options. Option types[{option_types}]" + for i, option in enumerate(one_of): + value, loc_comment, loc_title = self._get_schema_value(option, None) + ret.append(value) + ret.yaml_add_eol_comment( + f"[Example of possible configuration #{i}] {loc_comment}", key=i ) - ret.yaml_set_comment_before_after_key( - key=0, - before=self._get_title_block(title), - indent=SPSDK_YML_INDENT * (self.indent - 1), - ) + if loc_title: + ret.yaml_set_comment_before_after_key( + key=i, + before=self._get_title_block(loc_title), + indent=SPSDK_YML_INDENT * (self.indent - 1), + ) + ret.yaml_set_comment_before_after_key( + key=0, + before=self._get_title_block(title), + indent=SPSDK_YML_INDENT * (self.indent - 1), + ) self.indent -= 1 return ret def _get_schema_value( - self, - block: Dict[str, Any], - ) -> Tuple[Optional[Union[CM, CS, str, int, float, List]], Optional[str], Optional[str]]: + self, block: Dict[str, Any], custom_value: Any + ) -> Tuple[Optional[Union[CMap, CSeq, str, int, float, List]], Optional[str], Optional[str]]: """Private function used to fill up configuration block with data. :param block: Source block with data - :param order_list: Optional list with right order of records. + :param custom_value: value to be saved instead of default value :return: CM/CS base configuration object with comment :raises SPSDKError: In case of invalid data pattern. """ - schema_type = block.get("type") title = None - ret: Optional[Union[CM, CS, str, int, float, List]] = None - if "oneOf" in block.keys(): - ret = self._create_one_of_block(block) + schema_type = block.get("type") + assert schema_type, f"Type not available in block: {block}" + + if schema_type == "object": + assert (custom_value is None) or isinstance(custom_value, dict) + ret = self._create_object_block(block, custom_value) # type: ignore + elif schema_type == "array": + assert (custom_value is None) or isinstance(custom_value, list) + ret = self._create_array_block(block, custom_value) # type: ignore else: - assert schema_type, f"Type not available in block: {block}" - - if schema_type == "object": - ret = self._create_object_block(block) # type: ignore - elif schema_type == "array": - ret = self._create_array_block(block) # type: ignore - else: - assert "template_value" in block.keys() - ret = block.get("template_value") + assert "template_value" in block.keys() + ret = custom_value if (custom_value is not None) else block.get("template_value") - assert isinstance(ret, (CM, CS, str, int, float, list)) or ret is None + assert isinstance(ret, (CMap, CSeq, str, int, float, list)) or (ret is None) p_title = block.get("title", "") p_descr = block.get("description", "") @@ -402,8 +464,15 @@ def _get_schema_value( if "template_title" in block.keys(): title = block["template_title"] if p_enum: - p_descr = f"{p_descr}{', 'if p_descr else ''}Possible options:{p_enum}" - comment = f"{p_title}{', ' if p_descr else ''}{p_descr}" + if p_descr.endswith("."): + p_descr = p_descr[:-1] + p_descr = f"{p_descr}{'; ' if p_descr else ''}Possible options:{p_enum}" + if p_title.endswith("."): + p_title = p_title[:-1] + if p_descr.lower().startswith(p_title.lower()): + comment = p_descr + else: + comment = f"{p_title}{'; ' if p_descr else ''}{p_descr}" return ret, comment, title @staticmethod @@ -422,7 +491,7 @@ def _get_schema_block_keys(schema: Dict[str, Dict[str, Any]]) -> List[str]: or schema["properties"][key]["skip_in_template"] is False ] - def export(self) -> CM: + def export(self) -> CMap: """Export configuration template into CommentedMap. :raises SPSDKError: Error @@ -460,15 +529,23 @@ def export(self) -> CM: schemas_merger.merge(merged, copy.deepcopy(schema)) # 3. Create order of individual settings - order_list: List[str] = [] - for info in block_list.values(): - order_list.extend(info["properties"]) + order_dict: Optional[Dict[str, Any]] = None + if self.export_template and self.values: + properties_for_template = self._get_schema_block_keys(merged) + # create ordered dict with all properties and optionally override values + order_dict = OrderedDict() + for info in block_list.values(): + for p in info["properties"]: + if p in properties_for_template: + order_dict[p] = self.values.get(p, None) + else: + order_dict = self.values try: self.indent = 0 # 4. Go through all individual logic blocks - cfg = self._create_object_block(merged, order_list) - assert isinstance(cfg, CM) + cfg = self._create_object_block(merged, order_dict) + assert isinstance(cfg, CMap) # 5. Add main title of configuration title = f"=========== {self.main_title} ===========\n" if self.note: @@ -490,10 +567,10 @@ def export_to_yaml(self) -> str: :return: YAML string. """ - return ConfigTemplate.convert_cm_to_yaml(self.export()) + return self.convert_cm_to_yaml(self.export()) @staticmethod - def convert_cm_to_yaml(config: CM) -> str: + def convert_cm_to_yaml(config: CMap) -> str: """Convert Commented Map for into final YAML string. :param config: Configuration in CM format. @@ -522,12 +599,17 @@ def get_schema_file(sch_file: str) -> Dict[str, Any]: :return: Loaded schema file. """ abs_path = os.path.abspath(sch_file) - if not abs_path in ValidationSchemas._instancies: + if abs_path not in ValidationSchemas._instancies: try: - with open(abs_path) as f: - schema_cfg = YAML(typ="safe").load(f) + schema_cfg = load_configuration(abs_path) except (FileNotFoundError, YAMLError, UnicodeDecodeError) as exc: raise SPSDKError("Invalid validation scheme configuration file.") from exc ValidationSchemas._instancies[abs_path] = schema_cfg return ValidationSchemas._instancies[abs_path] + + +class ConfigTemplate(CommentedConfig): + """Deprecated, kept for backward compatibility only.""" + + pass diff --git a/spsdk/utils/serial_buspal_proxy.py b/spsdk/utils/serial_buspal_proxy.py index 7f4bf0da..2dfafcf9 100644 --- a/spsdk/utils/serial_buspal_proxy.py +++ b/spsdk/utils/serial_buspal_proxy.py @@ -14,6 +14,7 @@ Type, ) +from spsdk.exceptions import SPSDKError from spsdk.mboot.interfaces.buspal_i2c import I2cModeCommand from spsdk.mboot.interfaces.buspal_spi import SpiModeCommand @@ -38,14 +39,14 @@ def init_buspal_proxy(cls, target: str, data: Dict[bytes, bytes]) -> "Type[Seria :param target: BUSPAL target type :param data: Dictionary of write and read bytes :return: SerialProxy class with configured data - :raise AttributeError: target not supported + :raises SPSDKError: target not supported """ if target == "i2c": cls.frame_header = I2cModeCommand.write_then_read.value elif target == "spi": cls.frame_header = SpiModeCommand.write_then_read.value else: - raise AttributeError(f"Target {target} not supported") + raise SPSDKError(f"Target {target} not supported") return super().init_proxy(data) def __init__(self, port: str, timeout: int, baudrate: int, write_timeout: Optional[int] = None): diff --git a/tests/apps/data/certgen_config.yaml b/tests/apps/data/certgen_config.yaml index fa619afb..052aa3d1 100644 --- a/tests/apps/data/certgen_config.yaml +++ b/tests/apps/data/certgen_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + issuer: COMMON_NAME: ONE diff --git a/tests/crypto/test_sign_provider.py b/tests/crypto/test_sign_provider.py index cc726981..6ba7aedf 100644 --- a/tests/crypto/test_sign_provider.py +++ b/tests/crypto/test_sign_provider.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2021 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause """Tests for Signature Provider interface.""" from os import path -from spsdk.crypto import SignatureProvider +from spsdk.crypto.signature_provider import SignatureProvider def test_types(): @@ -27,7 +27,7 @@ def test_invalid_sp_type(): def test_plain_file(data_dir): - my_key_path = path.join(data_dir, "priv.pem") + my_key_path = path.join(data_dir, "priv.pem").replace("\\", "/") provider = SignatureProvider.create(f"type=file;file_path={my_key_path}") assert provider.sp_type == "file" diff --git a/tests/dat/data/dck_rsa2048_rot_meta_cert.yml b/tests/dat/data/dck_rsa2048_rot_meta_cert.yml index ee230d46..36586ba9 100644 --- a/tests/dat/data/dck_rsa2048_rot_meta_cert.yml +++ b/tests/dat/data/dck_rsa2048_rot_meta_cert.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_rsa2048.yml b/tests/dat/data/new_dck_rsa2048.yml index 8402ef3f..52c169a5 100644 --- a/tests/dat/data/new_dck_rsa2048.yml +++ b/tests/dat/data/new_dck_rsa2048.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_rsa2048_invalid.yml b/tests/dat/data/new_dck_rsa2048_invalid.yml index 11292dac..02ec5932 100644 --- a/tests/dat/data/new_dck_rsa2048_invalid.yml +++ b/tests/dat/data/new_dck_rsa2048_invalid.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_secp256.yml b/tests/dat/data/new_dck_secp256.yml index 57706c70..3e215970 100644 --- a/tests/dat/data/new_dck_secp256.yml +++ b/tests/dat/data/new_dck_secp256.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_secp256_lpc55s3x.yml b/tests/dat/data/new_dck_secp256_lpc55s3x.yml index 09431db7..cc932942 100644 --- a/tests/dat/data/new_dck_secp256_lpc55s3x.yml +++ b/tests/dat/data/new_dck_secp256_lpc55s3x.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0004 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_secp256_lpc55s3x_not_empty.yml b/tests/dat/data/new_dck_secp256_lpc55s3x_not_empty.yml index da795615..52feddef 100644 --- a/tests/dat/data/new_dck_secp256_lpc55s3x_not_empty.yml +++ b/tests/dat/data/new_dck_secp256_lpc55s3x_not_empty.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0004 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_secp384_lpc55s3x.yml b/tests/dat/data/new_dck_secp384_lpc55s3x.yml index 5ec2d1e5..353252d7 100644 --- a/tests/dat/data/new_dck_secp384_lpc55s3x.yml +++ b/tests/dat/data/new_dck_secp384_lpc55s3x.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0004 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/new_dck_secp384_lpc55s3x_not_empty.yml b/tests/dat/data/new_dck_secp384_lpc55s3x_not_empty.yml index 479014dc..9930da4e 100644 --- a/tests/dat/data/new_dck_secp384_lpc55s3x_not_empty.yml +++ b/tests/dat/data/new_dck_secp384_lpc55s3x_not_empty.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0004 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/no_key_dck_rsa_2048.yml b/tests/dat/data/no_key_dck_rsa_2048.yml index 058eaf21..e8e6efb5 100644 --- a/tests/dat/data/no_key_dck_rsa_2048.yml +++ b/tests/dat/data/no_key_dck_rsa_2048.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/org_dck_rsa_2048.yml b/tests/dat/data/org_dck_rsa_2048.yml index 3aff326a..771d628a 100644 --- a/tests/dat/data/org_dck_rsa_2048.yml +++ b/tests/dat/data/org_dck_rsa_2048.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/plugin_dck_rsa_2048.yml b/tests/dat/data/plugin_dck_rsa_2048.yml index e1abe045..bb2af5fb 100644 --- a/tests/dat/data/plugin_dck_rsa_2048.yml +++ b/tests/dat/data/plugin_dck_rsa_2048.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + socc: 0x0001 uuid: "E004090E6BDD2155BBCE9E0665805BE3" cc_socu: 0x03FF diff --git a/tests/dat/data/signature_provider.py b/tests/dat/data/signature_provider.py index 8181e378..954b9699 100644 --- a/tests/dat/data/signature_provider.py +++ b/tests/dat/data/signature_provider.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2021 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause -from spsdk.crypto import SignatureProvider +from spsdk.crypto.signature_provider import SignatureProvider class TestSignatureProvider(SignatureProvider): @@ -14,10 +14,6 @@ class TestSignatureProvider(SignatureProvider): def __init__(self, param: str) -> None: self.param = int(param) - def info(self) -> str: - msg = "Test Signature provider" - msg += f"param: {self.param}" - def sign(self, data: bytes) -> bytes: return b"x" * self.param diff --git a/tests/dat/test_dar_packet.py b/tests/dat/test_dar_packet.py index 7fc160d5..bd3eeb64 100644 --- a/tests/dat/test_dar_packet.py +++ b/tests/dat/test_dar_packet.py @@ -38,7 +38,6 @@ def test_dar_packet_rsa( assert dc.VERSION == DAC.parse(dac_bytes).version, "Version of DC and DAC are different." dar = DebugAuthenticateResponse.create( version=version, - socc=dc.socc, dc=dc, auth_beacon=0, dac=DAC.parse(dac_bytes), @@ -66,7 +65,6 @@ def test_dar_packet_lpc55s3x_256(data_dir, yml_file_name, version, file_key, exp dc.sign() dar = DebugAuthenticateResponse.create( version=version, - socc=dc.socc, dc=dc, auth_beacon=0, dac=DAC.parse(dac_bytes), diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_256_256.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_256_256.json new file mode 100644 index 00000000..7eefdd0c --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_256_256.json @@ -0,0 +1,15 @@ +{ + "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp256r1_cert0.pem", + "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp256r1_cert1.pem", + "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp256r1_cert2.pem", + "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp256r1_cert3.pem", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_cert0.pem", + "useIsk": true, + "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp256r1_sign_cert.pem", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "signingCertificateConstraint": "0x0000", + "signCertData": ".\\workspace\\input_images\\testfffffff.bin", + "timestamp": "0x123456", + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\cert_256_256.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_256.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_256.json new file mode 100644 index 00000000..0257537b --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_256.json @@ -0,0 +1,15 @@ +{ + "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp384r1_cert0.pem", + "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp384r1_cert1.pem", + "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp384r1_cert2.pem", + "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp384r1_cert3.pem", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_cert0.pem", + "useIsk": true, + "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp256r1_sign_cert.pem", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "signingCertificateConstraint": "0x25", + "iskCertificateEllipticCurve": "secp256r1", + "signCertData": "", + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\cert_384_256.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_384.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_384.json new file mode 100644 index 00000000..29a4581c --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/cert_384_384.json @@ -0,0 +1,15 @@ +{ + "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp384r1_cert0.pem", + "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp384r1_cert1.pem", + "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp384r1_cert2.pem", + "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp384r1_cert3.pem", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_cert0.pem", + "useIsk": true, + "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp384r1_sign_cert.pem", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_sign_cert.pem", + "signingCertificateConstraint": "0x25", + "iskCertificateEllipticCurve": "secp384r1", + "signCertData": ".\\workspace\\input_images\\testfffffff.bin", + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\cert_384_384.bin" +} \ No newline at end of file diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert.json new file mode 100644 index 00000000..126c2f87 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert.json @@ -0,0 +1,12 @@ +{ + "family": "lpc55s3x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "xip", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "signed", + "trustZonePresetFile": "", + "firmwareVersion": "0x1", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_384_256.bin", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_384_256.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert_invalid.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert_invalid.json new file mode 100644 index 00000000..cd1dfdbc --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_256_cert_invalid.json @@ -0,0 +1,31 @@ +{ + "family": "lpc55s3x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "xip", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "signed", + "trustZonePresetFile": "", + "firmwareVersion": "0x1", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_384_256.bin", + "rootCertificate0File": ".\\workspace\\keys_certs\\ec_secp384r1_cert0.pem", + "rootCertificate1File": ".\\workspace\\keys_certs\\ec_secp384r1_cert1.pem", + "rootCertificate2File": ".\\workspace\\keys_certs\\ec_secp384r1_cert2.pem", + "rootCertificate3File": ".\\workspace\\keys_certs\\ec_secp384r1_cert3.pem", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_cert0.pem", + "rootCertificateEllipticCurve": "secp384r1", + "useIsk": true, + "signingCertificateFile": ".\\workspace\\keys_certs\\ec_secp256r1_sign_cert.pem", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "signingCertificateConstraint": "0x25", + "iskCertificateEllipticCurve": "secp256r1", + "signCertData": "", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_384_256.bin", + "testCertBlockMagic": "", + "testCorruptRkhRecord": false, + "testCorruptRkhRecordId": 0, + "testCorruptIskSignature": false, + "testImageManifestMagic": "", + "testImageType": false, + "testImageTypeValue": 255 +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384_cert.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384_cert.json new file mode 100644 index 00000000..d6de1970 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/mb_xip_384_384_cert.json @@ -0,0 +1,12 @@ +{ + "family": "lpc55s3x", + "inputImageFile": ".\\workspace\\input_images\\normal_boot.bin", + "outputImageExecutionTarget": "xip", + "outputImageExecutionAddress": "0x0", + "outputImageAuthenticationType": "signed", + "trustZonePresetFile": "", + "firmwareVersion": "0x1", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_sign_cert.pem", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_384_384.bin", + "masterBootOutputFile": ".\\workspace\\output_images\\lpc55s3x\\mb_xip_384_384.bin" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_cert.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_cert.json new file mode 100644 index 00000000..d4a01c73 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_256_256_cert.json @@ -0,0 +1,35 @@ +{ + "family": "lpc55s3x", + "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", + "isNxpContainer": false, + "description": "sb3_256_256.sb3", + "kdkAccessRights": 3, + "containerConfigurationWord": "0x0", + "firmwareVersion": "0x1", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_256_256.bin", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "rootCertificateEllipticCurve": "secp256r1", + "testSb3Magic": "", + "testSb3ImageType": false, + "testSb3ImageTypeValue": 255, + "testCertBlockMagic": "", + "testCorruptRkhRecord": false, + "testCorruptRkhRecordId": 0, + "testCorruptIskSignature": false, + "timestamp": "0x123456", + "commands": [ + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } + ], + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_256_256.sb3" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_cert.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_cert.json new file mode 100644 index 00000000..1db8a659 --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_256_cert.json @@ -0,0 +1,35 @@ +{ + "family": "lpc55s3x", + "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", + "isNxpContainer": false, + "description": "sb3_384_256.sb3", + "kdkAccessRights": 3, + "containerConfigurationWord": "0x0", + "firmwareVersion": "0x1", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_384_256.bin", + "rootCertificateEllipticCurve": "secp384r1", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp256r1_sign_cert.pem", + "timestamp": "0x123456", + "testSb3Magic": "", + "testSb3ImageType": false, + "testSb3ImageTypeValue": 255, + "testCertBlockMagic": "", + "testCorruptRkhRecord": false, + "testCorruptRkhRecordId": 0, + "testCorruptIskSignature": false, + "commands": [ + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + }, + { + "load": { + "address": "0x0", + "file": ".\\workspace\\output_images\\lpc55s3x\\normal_boot_sign.bin" + } + } + ], + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_256.sb3" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384_cert.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384_cert.json new file mode 100644 index 00000000..5d46a69b --- /dev/null +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_384_384_cert.json @@ -0,0 +1,29 @@ +{ + "family": "lpc55s3x", + "containerKeyBlobEncryptionKey": ".\\workspace\\keys\\userkey.txt", + "isNxpContainer": true, + "description": "sb3_384_384.sb3", + "kdkAccessRights": 3, + "containerConfigurationWord": "0x0", + "firmwareVersion": "0x1", + "binaryCertificateBlock": ".\\workspace\\output_images\\lpc55s3x\\cert_384_384.bin", + "rootCertificateEllipticCurve": "secp384r1", + "signingCertificatePrivateKeyFile": ".\\workspace\\keys_certs\\ec_pk_secp384r1_sign_cert.pem", + "timestamp": "0x123456", + "testSb3Magic": "", + "testSb3ImageType": false, + "testSb3ImageTypeValue": 255, + "testCertBlockMagic": "", + "testCorruptRkhRecord": false, + "testCorruptRkhRecordId": 0, + "testCorruptIskSignature": false, + "commands": [ + { + "erase": { + "address": "0x0", + "size": "0x10000" + } + } + ], + "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_384_384_nxp.sb3" +} diff --git a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json index 7e379b13..34053265 100644 --- a/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json +++ b/tests/elftosb/data/workspace/cfgs/lpc55s3x/sb3_test_384_384_unencrypted.json @@ -102,16 +102,6 @@ "file": ".\\workspace\\input_ifr\\ifr2.bin" } }, - { - "call": { - "address": "0x1384" - } - }, - { - "call": { - "address": "0x2588" - } - }, { "execute": { "address": "0x1384" @@ -165,4 +155,4 @@ } ], "containerOutputFile": ".\\workspace\\output_images\\lpc55s3x\\sb3_test_384_384_unencrypted.sb3" -} +} \ No newline at end of file diff --git a/tests/elftosb/data/workspace/output_images/lpc55s3x/cert_256_256.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/cert_256_256.bin new file mode 100644 index 0000000000000000000000000000000000000000..ebf2e573b8fef8fad1128075d5f46d84cd4e064f GIT binary patch literal 716 zcmYe!NGW1uU}8AK#K7Rlz`&3$XJ5#XBw=u%_k)e`T%DH%JY3$_{i}bU@%a6yreZ&9 zIa@?gkq>WpnA#O*R?n`*8?S{uykmdL_r-CkxQz8`-EJ6)NxGNWM1T>{8UU)r$MOWh6M*hai=9?|zQp>$QMHi_A#65erG|lRI0MoV9hi5(} zUEFAhCK?{1!gpmp|XXWH}qok@~8B z=}n!FpZe6k&70-2hY{!rAOKPgkK`w-Y1vz<*KNPXt=}+3*huQ-mzVO%E_vTd>bQT@ z-!Lh+-x~fr-F9{Gp1t#G8lG_6dHXwd(f?*u`%8~jr)J*!KMLp?0(M8GSgo$DwanP< zaIm*~UFr(IgU0=gB_+?B$00IEq0RR9(0000^iMc-eB>>975H?A7ntDbV(P5-C#d?JiX%`UQ z|9EVZ3%DUk>LzC9jfUi25}gJO&g@_*bmsn0R%*sQ;=YtEFCP&ZG9+&x7(0i0r^N2J zHFxR1;rt_Y;csJQA~$#UBR$?)YFZ8_0y7{-2l{{>Ywr=QVS@7c>@&{-`i6Z|Hvh`fA)d8({cM z0000b000010002uACo33KRG9Ux7G_UfRqz58tL@uA7eyr_H=#=_Khol>Woh0#j0s@u@M=k9LKX%49Yjy9s+ybY4Or{U1}+@; z^gadJU~nhNH7G^4oVE1<)!AbVXY70a?=65ix-QqR#xMeOlScXMm{!qabwBC}*KK%D Szne0TJz$i8kF21GJ#!{mncDpT literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s3x/cert_384_384.bin b/tests/elftosb/data/workspace/output_images/lpc55s3x/cert_384_384.bin new file mode 100644 index 0000000000000000000000000000000000000000..c1abfc92e34dce34bfe5aec0454e3ccdb2e4f895 GIT binary patch literal 876 zcmYe!NGW1uU}DH&W?*n)U|{g;+-dh)mEqJu0W;6?SygV57ZaD59Ia{<%oG>6_rD@% zGWQNe&sS<`&wAUQ#0kx1<30B#L9^u9zksl;W44d>P0`ht7nC$o$yboDZm(K?0N*KaPyv!JCM?1!nmd;QJhh?_+i9?mVMiDW?R3#v^$IKY7^gl zwcagdR~F8EecSMsU<>CjvFe51Pt+P!Pb`uDH-CoIzZ3H+pPLr%5jPR*{GtnVKFI&y#`Mdl4Jue@W008Y-TlD|{ literal 0 HcmV?d00001 diff --git a/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384_unencrypted.sb3 b/tests/elftosb/data/workspace/output_images/lpc55s3x/sb3_test_384_384_unencrypted.sb3 index 6bf7914be0f71a1c5ae12c223406ff67450a66d5..997f329605201a931f6bb848c542a0111d07a548 100644 GIT binary patch delta 4339 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 delta 4356 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πiB9c|Udfi{W`a4>8~yOxeboK8I3$?JaH&m--rox_ z!QK@qK#s*8hS>`|%~_u-;yS{QU>DaNa2G%Ldu|$TNxDL16>a`%L!skE8b}lG~%sBMLpkQ9o9Mj zIK8f?!6s#ZWO`bZNuIvIXI2JouDvU}A;Pb_^G-v@%~fCZWUlOPjT>uJC)EmlvZSf9 zVH??`sm4lKKz^Zke9BoLkR=a`QS97Kug_rm0-nXYI0|NpJE@x4c;FJDAZ)*${5kKZ zie6Y}U@un60kT_aLfd{ihmPljIx%&i%uP~WWIZ{fA(Us}gIlC?lVdCY3`AsEsp>mt zu~BO=g5Y}0D>xVmN$uH}?ncb`F}GLc%k_pN)Yv?0Q(!&M$QXIwA@ysUVOAwxKYuzs z(jS%r$p^p0CKZ4ryvsMqdW{3VzD-Jtk&zZAiyIfo9jqfoq(YQ0Tk1v>PkDxAS86r2 zqE)3)5V$W^DgyHM?$>#i23HwpPXr}t^3ezhsi~w%?P;h|R0$du5|i-QurOcb4@!f< zTCtvC<@o>Kff67IaQx!b<(Kn)LN_$1nxfrM+{)CDff^}4HOgN(IE}n0$9m?~NrQv} zi@+t#oj#fCF~{`4P+bH*!oi+h2sWyngY3NNsatrX)u_2K4SAJ!CM&eIfHI<$o|2Ps zi;xW*vD))(!yZ-{NZyk^a+C1CEHz$$k}zeH(m+P!*3t8W`_T2|1@~YjuF-aC zFSiyqyrE1RD>UcU>Y5$0g1H|9zyGKps>B&BOK@4?@}fFQHr-kA?2CG$n_(*gEX z42sm}5XKg<{fa;OVUC_CaM(*=8EF2yw6yT3XLuoD8YH_-QaQ9$7Of6J9YrIqoHMSD zy>N!+hNPD}PbP^_o}*;w9e)2@^sGe%H3)#F<>jVP|K1dU4+!mL3B!e>$GL8yn=RSV MrIs{k_WPs%0Smm%A^-pY diff --git a/tests/elftosb/test_bd_compiler.py b/tests/elftosb/test_bd_compiler.py index e0a0cb94..ab135399 100644 --- a/tests/elftosb/test_bd_compiler.py +++ b/tests/elftosb/test_bd_compiler.py @@ -413,13 +413,13 @@ def test_extern(input_text, throws_exception, extern): r"""section (1;id=5) { }""", - True, + False, ), ( r"""section (1;id=5, bla=7) { }""", - True, + False, ), ], ) @@ -1268,6 +1268,110 @@ def test_unary_expr(input_text, throws_exception): assert exception_thrown == throws_exception +def test_csf_sections(): + """""" + bd_file = r""" + options { + flags = 0x08; + startAddress = 0x30000000; + ivtOffset = 0x1000; + initialLoadSize = 0x2000; + entryPointAddress = 0x300024e1; + } + + + constants { + SEC_CSF_HEADER = 20; + SEC_CSF_INSTALL_SRK = 21; + SEC_CSF_INSTALL_CSFK = 22; + SEC_CSF_INSTALL_NOCAK = 23; + SEC_CSF_AUTHENTICATE_CSF = 24; + SEC_CSF_INSTALL_KEY = 25; + SEC_CSF_AUTHENTICATE_DATA = 26; + SEC_CSF_INSTALL_SECRET_KEY = 27; + SEC_CSF_DECRYPT_DATA = 28; + SEC_NOP = 29; + SEC_SET_MID = 30; + SEC_SET_ENGINE = 31; + SEC_INIT = 32; + SEC_UNLOCK = 33; + } + + section (SEC_CSF_HEADER; + Header_Version="4.2", + Header_HashAlgorithm="sha256", + Header_Engine="ANY", + Header_EngineConfiguration=0, + Header_CertificateFormat="x509", + Header_SignatureFormat="CMS" + ) + { + } + + section (SEC_SET_ENGINE; + SetEngine_HashAlgorithm = "sha256", + SetEngine_Engine = "ANY", + SetEngine_EngineConfiguration = "0") + { + } + + section (SEC_UNLOCK; + Unlock_Engine = "SNVS", + Unlock_features = "ZMK WRITE" + ) + { + } + """ + + expected_result = { + "options": { + "flags": 8, + "startAddress": 805306368, + "ivtOffset": 4096, + "initialLoadSize": 8192, + "entryPointAddress": 805315809, + }, + "sections": [ + { + "section_id": 20, + "options": [ + {"Header_Version": "4.2"}, + {"Header_HashAlgorithm": "sha256"}, + {"Header_Engine": "ANY"}, + {"Header_EngineConfiguration": 0}, + {"Header_CertificateFormat": "x509"}, + {"Header_SignatureFormat": "CMS"}, + ], + "commands": [], + }, + { + "section_id": 31, + "options": [ + {"SetEngine_HashAlgorithm": "sha256"}, + {"SetEngine_Engine": "ANY"}, + {"SetEngine_EngineConfiguration": "0"}, + ], + "commands": [], + }, + { + "section_id": 33, + "options": [{"Unlock_Engine": "SNVS"}, {"Unlock_features": "ZMK WRITE"}], + "commands": [], + }, + ], + } + + parser = bd_parser.BDParser() + + try: + result = parser.parse(bd_file) + exception_thrown = False + assert expected_result == result + except spsdk.SPSDKError: + exception_thrown = True + assert exception_thrown == False + + # TODO document from_stmt - change syntax to SOURCE_NAME # TODO describe format of section name # TODO load_data ::= int_const_expr identifier is not allowed!!! diff --git a/tests/elftosb/test_elftosb_cfg_template.py b/tests/elftosb/test_elftosb_cfg_template.py index b4e5f68a..36fcfc06 100644 --- a/tests/elftosb/test_elftosb_cfg_template.py +++ b/tests/elftosb/test_elftosb_cfg_template.py @@ -35,4 +35,4 @@ def test_elftosb_cfgtmp_create(tmpdir, device): result = runner.invoke(elftosb.main, cmd.split()) assert result.exit_code == 0 # Check at least common TrustZone Configuration file - assert os.path.isfile(os.path.join(tmpdir, f"{device}_tz.yml")) + assert os.path.isfile(os.path.join(tmpdir, f"{device}_tz.yaml")) diff --git a/tests/elftosb/test_elftosb_sb31.py b/tests/elftosb/test_elftosb_sb31.py index b5804f37..0fa217ac 100644 --- a/tests/elftosb/test_elftosb_sb31.py +++ b/tests/elftosb/test_elftosb_sb31.py @@ -21,7 +21,6 @@ def process_config_file( config_path: str, destination: str, config_member: str ) -> Tuple[str, str, str]: - config_data = load_configuration(config_path) for key in config_data: if isinstance(config_data[key], str): @@ -70,7 +69,6 @@ def test_elftosb_sb31(data_dir, tmpdir, config_file, device): def test_elftosb_sb31_notime(data_dir, tmpdir): - config_file = "sb3_256_256.json" device = "lpc55s3x" runner = CliRunner() diff --git a/tests/elftosb/test_lexer.py b/tests/elftosb/test_lexer.py index d0b31b32..da2a5cd1 100644 --- a/tests/elftosb/test_lexer.py +++ b/tests/elftosb/test_lexer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -131,7 +131,6 @@ def test_lexer(): ], ) def test_source_name(input_text, expected_result): - lexer = BDLexer() lexer._sources.append(Variable("source_name", "source", "")) diff --git a/tests/ifr/data/template45.yaml b/tests/ifr/data/template45.yaml index cbbeeaca..8465d606 100644 --- a/tests/ifr/data/template45.yaml +++ b/tests/ifr/data/template45.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP kw45xx PFR ROMCFG configuration description: # The ROMCFG configuration description. device: kw45xx # The NXP device name. diff --git a/tests/image/ahab/test_ahab.py b/tests/image/ahab/test_ahab.py index 7e3ec45c..7e4f0404 100644 --- a/tests/image/ahab/test_ahab.py +++ b/tests/image/ahab/test_ahab.py @@ -123,7 +123,7 @@ def ahab_container(request): @pytest.fixture(scope="function") def ahab_image(request): - return AHABImage(family="rt1180", ahab_containers=[request.getfixturevalue("ahab_container")]) + return AHABImage(family="rt118x", ahab_containers=[request.getfixturevalue("ahab_container")]) def test_container_head_compare(container_head): diff --git a/tests/image/images/test_bootimage_rt10xx.py b/tests/image/images/test_bootimage_rt10xx.py index fd1e7b4d..e65a9142 100644 --- a/tests/image/images/test_bootimage_rt10xx.py +++ b/tests/image/images/test_bootimage_rt10xx.py @@ -1,11 +1,10 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2021 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause -from re import L import pytest diff --git a/tests/image/mbi/data/lpc55s6x_int_xip_plain.yml b/tests/image/mbi/data/lpc55s6x_int_xip_plain.yml index 8640f71f..e993f87a 100644 --- a/tests/image/mbi/data/lpc55s6x_int_xip_plain.yml +++ b/tests/image/mbi/data/lpc55s6x_int_xip_plain.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # =========== Master Boot Image Configuration template for lpc55s6x, Plain Image (either XIP or Load-to-RAM). =========== # # == Basic Settings == @@ -11,4 +15,4 @@ inputImageFile: tests/image/mbi/data/lpcxpresso55s36_led_blinky_ext_flash.s19 # # == Trust Zone Settings == # enableTrustZone: false # [Optional], TrustZone enable option, If not specified, the Trust zone is disabled. -trustZonePresetFile: my_tz_custom.yml # [Optional], TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used. +trustZonePresetFile: my_tz_custom.yaml # [Optional], TrustZone Customization file, If not specified, but TrustZone is enabled(enableTrustZone) the default values are used. diff --git a/tests/image/mbi/data/test_app_table.yaml b/tests/image/mbi/data/test_app_table.yaml index 0f0f9901..0112aa37 100644 --- a/tests/image/mbi/data/test_app_table.yaml +++ b/tests/image/mbi/data/test_app_table.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + applicationTable: - binary: tests/image/mbi/data/testfffffff.bin destAddress: "0x1234" diff --git a/tests/image/mbi/test_mbi.py b/tests/image/mbi/test_mbi.py index 77076b5c..b4281870 100644 --- a/tests/image/mbi/test_mbi.py +++ b/tests/image/mbi/test_mbi.py @@ -12,6 +12,8 @@ import pytest from spsdk import SPSDKError +from spsdk.apps.nxpimage import mbi_export +from spsdk.crypto.signature_provider import SignatureProvider from spsdk.image import ( MBIMG_SCH_FILE, MasterBootImage, @@ -32,7 +34,7 @@ get_all_mbi_classes, ) from spsdk.utils.crypto import CertBlockV2, Certificate -from spsdk.utils.misc import load_binary, load_configuration +from spsdk.utils.misc import load_configuration from spsdk.utils.schema_validator import ValidationSchemas, check_config ################################################################# @@ -78,15 +80,6 @@ def certificate_block(data_dir, der_file_names, index=0, chain_der_file_names=No return cert_block -def _load_private_key(data_dir: str, filename: str) -> bytes: - """Load PEM private key from data_dir - - :param filename: private key file name to load - :return: private key as binary data in PEM format, decrypted - """ - return load_binary(os.path.join(data_dir, "keys_and_certs", filename)) - - @pytest.mark.parametrize( "fw_ver, expected_val", [ @@ -186,13 +179,14 @@ def test_signed_xip_single_certificate_no_tz(data_dir, priv_key, der_certificate org_data = f.read() # create certification block cert_block = certificate_block(data_dir, [der_certificate]) - priv_key_pem_data = _load_private_key(data_dir, priv_key) + priv_key = os.path.join(data_dir, "keys_and_certs", priv_key) + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_SignedXip( app=org_data, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ) assert _compare_image(mbi, data_dir, expected_mbi) @@ -226,7 +220,10 @@ def test_signed_ram_single_certificate_no_tz(data_dir, user_key, key_store_filen org_data = f.read() # create certification block cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") + key_store = None if key_store_filename: with open(os.path.join(data_dir, key_store_filename), "rb") as f: @@ -238,7 +235,7 @@ def test_signed_ram_single_certificate_no_tz(data_dir, user_key, key_store_filen load_addr=0x12345678, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=user_key, key_store=key_store, ) @@ -287,14 +284,15 @@ def test_encrypted_ram_single_certificate_no_tz( ctr_init_vector = bytes.fromhex(ctr_iv) # create certification block cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_EncryptedRamRtxxx( app=org_data, load_addr=0x12345678, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=user_key, key_store=key_store, ctr_init_vector=ctr_init_vector, @@ -310,13 +308,14 @@ def test_encrypted_random_ctr_single_certificate_no_tz(data_dir): user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" key_store = KeyStore(KeySourceType.KEYSTORE, None) cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_EncryptedRamRtxxx( app=org_data, load_addr=0x12345678, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=user_key, key_store=key_store, ) @@ -364,13 +363,14 @@ def test_signed_xip_multiple_certificates_no_tz( org_data = f.read() # create certification block cert_block = certificate_block(data_dir, der_certificates, root_index) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_SignedXip( app=org_data, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ) assert _compare_image(mbi, data_dir, expected_mbi) @@ -400,13 +400,14 @@ def test_signed_xip_multiple_certificates_invalid_input(data_dir): # public key in certificate and private key does not match der_file_names = ["selfsign_4096_v3.der.crt"] cert_block = certificate_block(data_dir, der_file_names, 0) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") with pytest.raises(SPSDKError): Mbi_SignedXip( app=bytes(range(128)), trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ).export() # chain of certificates does not match @@ -449,13 +450,14 @@ def test_signed_xip_certificates_chain_no_tz( org_data = f.read() # create certification block cert_block = certificate_block(data_dir, der_certificates, 0, chain_certificates) - priv_key_pem_data = _load_private_key(data_dir, priv_key) + priv_key = os.path.join(data_dir, "keys_and_certs", priv_key) + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_SignedXip( app=org_data, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ) assert _compare_image(mbi, data_dir, expected_mbi) @@ -641,13 +643,14 @@ def test_master_boot_image_invalid_hmac(data_dir): user_key = "E39FD7AB61AE6DDDA37158A0FC3008C6D61100A03C7516EA1BE55A39F546BAD5" key_store = KeyStore(KeySourceType.KEYSTORE, None) cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_EncryptedRamRtxxx( app=org_data, load_addr=0x12345678, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=user_key, key_store=key_store, ) @@ -662,21 +665,22 @@ def test_invalid_export_mbi(data_dir): key_store_bin = None key_store = KeyStore(KeySourceType.KEYSTORE, key_store_bin) cert_block = certificate_block(data_dir, ["selfsign_2048_v3.der.crt"]) - priv_key_pem_data = _load_private_key(data_dir, "selfsign_privatekey_rsa2048.pem") + priv_key = os.path.join(data_dir, "keys_and_certs", "selfsign_privatekey_rsa2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key}") mbi = Mbi_EncryptedRamRtxxx( app=org_data, load_addr=0x12345678, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=user_key, key_store=key_store, ctr_init_vector=bytes(16), ) - mbi.priv_key_data = None + mbi.signature_provider = None with pytest.raises(SPSDKError): mbi.export() - mbi.priv_key_data = priv_key_pem_data + mbi.signature_provider = signature_provider mbi.cert_block = None with pytest.raises(SPSDKError): mbi.export() diff --git a/tests/image/segments/test_xmcd.py b/tests/image/segments/test_xmcd.py index 2a7b0300..483b1856 100644 --- a/tests/image/segments/test_xmcd.py +++ b/tests/image/segments/test_xmcd.py @@ -6,6 +6,7 @@ # SPDX-License-Identifier: BSD-3-Clause from spsdk.image.images import BootImgRT +from spsdk.image.segments import XMCDHeader def test_parse_xmcd(data_dir): @@ -22,3 +23,16 @@ def test_parse_no_xmcd(data_dir): image = BootImgRT.parse(data) assert image.xmcd is None assert "XMCD" not in image.info() + + +def test_xmcd_header(): + data = b"\x04\x12\x00\xc0" + xmcd = XMCDHeader.parse(data) + assert xmcd.block_size == 516 + assert xmcd.block_type == 1 + assert xmcd.instance == 0 + assert xmcd.interface == 0 + assert xmcd.tag == 12 + assert xmcd.version == 0 + exported = xmcd.export() + assert exported == data diff --git a/tests/mboot/blhost/test_blhost_cli.py b/tests/mboot/blhost/test_blhost_cli.py index 4575d747..172e318b 100644 --- a/tests/mboot/blhost/test_blhost_cli.py +++ b/tests/mboot/blhost/test_blhost_cli.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -13,8 +13,6 @@ import spsdk from spsdk.apps import blhost -from spsdk.mboot.interfaces.buspal import BBConstants, BuspalMode, Response -from spsdk.mboot.interfaces.buspal_i2c import BuspalI2C, I2cModeCommand from spsdk.utils.misc import load_binary from spsdk.utils.serial_buspal_proxy import SerialBuspalProxy from spsdk.utils.serial_proxy import SerialProxy diff --git a/tests/mboot/devices/virtual_device.yaml b/tests/mboot/devices/virtual_device.yaml index 80f296bc..533b3307 100644 --- a/tests/mboot/devices/virtual_device.yaml +++ b/tests/mboot/devices/virtual_device.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + Properties: CurrentVersion: "K0.1.0" AvailablePeripherals: diff --git a/tests/mboot/test_commands.py b/tests/mboot/test_commands.py index 765187c7..ef695fd3 100644 --- a/tests/mboot/test_commands.py +++ b/tests/mboot/test_commands.py @@ -1,13 +1,11 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2021 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause -import pytest -from spsdk import SPSDKError from spsdk.mboot.commands import ( CmdHeader, CmdPacket, diff --git a/tests/mboot/test_mboot_api.py b/tests/mboot/test_mboot_api.py index 17b0f50b..aebd5bc4 100644 --- a/tests/mboot/test_mboot_api.py +++ b/tests/mboot/test_mboot_api.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2022 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -13,13 +13,11 @@ from spsdk.mboot.mcuboot import ( CmdPacket, CommandTag, - ExtMemId, KeyProvUserKeyType, McuBoot, PropertyTag, StatusCode, ) -from tests.mcu_examples.test_rt5xx import write_shadow_regis def test_class(mcuboot, target, config): diff --git a/tests/mboot/virtual_device.py b/tests/mboot/virtual_device.py index a5b22933..ca28818c 100644 --- a/tests/mboot/virtual_device.py +++ b/tests/mboot/virtual_device.py @@ -1,19 +1,19 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2022 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause import logging from struct import pack +from spsdk.exceptions import SPSDKError from spsdk.mboot.commands import ( CmdPacket, CommandTag, KeyProvOperation, ResponseTag, - TrustProvisioningResponse, TrustProvOperation, parse_cmd_response, ) @@ -351,7 +351,7 @@ def write(self, packet): self._cmd_data = packet raw_data = packet else: - raise Exception("Not valid packet type !") + raise SPSDKError("Not valid packet type !") logging.debug(f"RAW-OUT[{len(raw_data)}]: " + ", ".join(f"{b:02X}" for b in raw_data)) def info(self): diff --git a/tests/mcu_examples/test_rt10xx.py b/tests/mcu_examples/test_rt10xx.py index 412a9fe3..67b8820c 100644 --- a/tests/mcu_examples/test_rt10xx.py +++ b/tests/mcu_examples/test_rt10xx.py @@ -45,6 +45,7 @@ ) from spsdk.mboot import ExtMemId, McuBoot, PropertyTag from spsdk.mboot import scan_usb as mboot_scan_usb +from spsdk.mboot.exceptions import McuBootConnectionError from spsdk.sbfile.sb1 import ( BootSectionV1, CmdErase, @@ -122,6 +123,9 @@ def __init__( board: str, cpu_data: hab_audit_log.CpuData, ext_flash_cfg_word0: int, + encryption_supported: bool = True, + xip_signature_supported: bool = True, + none_xip_signature_supported: bool = True, ): """Constructor. @@ -131,6 +135,9 @@ def __init__( :param board: name of the board (used to select name of the source and output image) :param cpu_data: contains important specific data for each cpu :param ext_flash_cfg_word0: configuration word 0 for external FLASH + :param encryption_supported: Flag that CPU supports encryption HAB + :param xip_signature_supported: Flag that CPU supports XIP signed HAB + :param none_xip_signature_supported: Flag that CPU supports None XIP signed HAB """ # ID of the test configuration self.id = data_subdir @@ -148,6 +155,9 @@ def __init__( self.ext_flash_cfg_word0 = ext_flash_cfg_word0 self.ext_flash_cfg_word1 = 0 # currently zero for all RT self.cpu_data = cpu_data + self.encryption_supported = encryption_supported + self.xip_signature_supported = xip_signature_supported + self.none_xip_signature_supported = none_xip_signature_supported def __str__(self) -> str: return self.id + " " + self.__class__.__name__ @@ -162,6 +172,8 @@ def rt1020(cls) -> "CpuParams": "evkmimxrt1020", hab_audit_log.CpuData.MIMXRT1020, IS25WP_FLASH_CFG_WORD0, + encryption_supported=False, + none_xip_signature_supported=False, ) @classmethod @@ -331,7 +343,7 @@ def init_flashloader(cpu_params: CpuParams) -> McuBoot: :param cpu_params: processor specific parameters of the test :return: McuBoot instance to communicate with flash-loader - :raises ConnectionError: if connection cannot be established + :raises McuBootConnectionError: if connection cannot be established """ devs = mboot_scan_usb( cpu_params.com_processor_name @@ -344,13 +356,15 @@ def init_flashloader(cpu_params: CpuParams) -> McuBoot: devs = sdp_scan_usb(cpu_params.com_processor_name) if len(devs) != 1: - raise ConnectionError("Cannot connect to ROM bootloader") + raise McuBootConnectionError("Cannot connect to ROM bootloader") with SDP(devs[0], cmd_exception=True) as sd: assert sd.is_opened try: sd.read(INT_RAM_ADDR_CODE, 4) # dummy read to receive response - except SdpCommandError: # there is an exception if HAB is locked, cause read not supported + except ( + SdpCommandError + ): # there is an exception if HAB is locked, cause read not supported pass if (sd.status_code == StatusCode.HAB_IS_LOCKED) and ( @@ -799,6 +813,9 @@ def test_signed(cpu_params: CpuParams, srk_key_index: int) -> None: :param cpu_params: processor specific parameters of the test :param srk_key_index: index of the SRK key used """ + if not cpu_params.xip_signature_supported: + pytest.skip("Unsupported configuration") + image_name = f"{cpu_params.board}_iled_blinky_ext_FLASH" tgt_address = EXT_FLASH_ADDR @@ -815,6 +832,9 @@ def test_signed_flashloader(cpu_params: CpuParams) -> None: """ assert TEST_IMG_CONTENT # this should be used in test mode only to verify the flashloader image creation process + if not cpu_params.none_xip_signature_supported: + pytest.skip("Unsupported configuration") + image_name = "ivt_flashloader" tgt_address = INT_RAM_ADDR_CODE @@ -915,6 +935,9 @@ def test_hab_encrypted(cpu_params: CpuParams) -> None: """Test HAB encrypted image. :param cpu_params: processor specific parameters of the test """ + if not cpu_params.encryption_supported: + pytest.skip("Unsupported configuration") + image_name = f"{cpu_params.board}_iled_blinky_int_RAM" tgt_address = INT_RAM_ADDR_CODE srk_key_index = 0 diff --git a/tests/mcu_examples/test_rt5xx.py b/tests/mcu_examples/test_rt5xx.py index c626550e..c2bc014c 100644 --- a/tests/mcu_examples/test_rt5xx.py +++ b/tests/mcu_examples/test_rt5xx.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -14,6 +14,7 @@ import pytest from bitstring import BitArray +from spsdk.crypto.signature_provider import SignatureProvider from spsdk.image import KeySourceType, KeyStore, TrustZone from spsdk.image.mbimg import ( Mbi_CrcRamRtxxx, @@ -23,6 +24,7 @@ Mbi_PlainSignedXipRtxxx, ) from spsdk.mboot import ExtMemId, KeyProvUserKeyType, McuBoot, PropertyTag, scan_usb +from spsdk.mboot.exceptions import McuBootConnectionError from spsdk.sbfile.sb2.commands import CmdErase, CmdFill, CmdLoad, CmdMemEnable from spsdk.sbfile.sb2.images import BootImageV21, BootSectionV2, CertBlockV2, SBV2xAdvancedParams from spsdk.utils.crypto import Certificate, KeyBlob, Otfad @@ -141,7 +143,7 @@ def write_shadow_regis(data_dir: str, writes: List[Tuple[int, int]]) -> None: assert len(datatable_old) == 12 * 8 * 2 + 2 # this is new table content datatable_new = bytes() - for (addr, value) in writes: + for addr, value in writes: datatable_new += pack(" McuBoot: """Open USB communication with RT5xx boot-loader :return: McuBoot instance - :raises ConnectionError: if device not connected + :raises McuBootConnectionError: if device not connected """ assert not TEST_IMG_CONTENT @@ -180,7 +182,7 @@ def open_mboot() -> McuBoot: sleep(1) if len(devs) != 1: - raise ConnectionError( + raise McuBootConnectionError( "RT5xx not connected via USB, " "ensure BOOT CONFIG SW7 is ON,OFF,ON and connect USB cable via J38" ) @@ -203,7 +205,7 @@ def burn_img_via_usb_into_flexspi_flash(data_dir: str, img_data: bytes) -> Optio :param data_dir: absolute path where the data files are located :param img_data: binary image data :return: McuBoot instance to talk to processor bootloader; None in test mode - :raise ConnectionError: if USB connection with processor's boot-loader cannot be established + :raises ConnectionError: if USB connection with processor's boot-loader cannot be established """ if TEST_IMG_CONTENT: # this function communicates with HW board, it cannot be used in test mode return None @@ -242,7 +244,7 @@ def send_sb_via_usb_into_processor(sb_data: bytes, key_store: KeyStore) -> None: :param sb_data: SB file to be sent :param key_store: key-store used for SB file - :raise ConnectionError: if USB connection with processor's boot-loader cannot be established + :raises ConnectionError: if USB connection with processor's boot-loader cannot be established """ if TEST_IMG_CONTENT: # this function communicates with HW board, it cannot be used in test mode return @@ -349,13 +351,11 @@ def get_keystore(data_dir: str) -> KeyStore: return KeyStore(KeySourceType.KEYSTORE, key_store_bin) -def create_cert_block(data_dir: str) -> Tuple[CertBlockV2, bytes]: +def create_cert_block(data_dir: str) -> CertBlockV2: """Load 4 certificates and create certificate block :param data_dir: absolute path - :return: tuple with the following items: - - certificate block with 4 certificates, certificate 0 is selected - - private key 0, decrypted binary data in PEM format + :return: certificate block with 4 certificates, certificate 0 is selected """ # load certificates cert_path = os.path.join(data_dir, "keys_certs") @@ -372,9 +372,13 @@ def create_cert_block(data_dir: str) -> Tuple[CertBlockV2, bytes]: for root_key_index, cert in enumerate(cert_list): if cert: cert_block.set_root_key_hash(root_key_index, cert) - # private key that matches selected root certificate - priv_key_pem_data = load_binary(os.path.join(cert_path, "k0_cert0_2048.pem")) - return cert_block, priv_key_pem_data + return cert_block + + +def create_signature_provider(data_dir: str) -> SignatureProvider: + priv_key_pem_path = os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem") + signature_provider = SignatureProvider.create(f"type=file;file_path={priv_key_pem_path}") + return signature_provider ####################################################################################################################### @@ -449,7 +453,8 @@ def test_ram_signed_otp(data_dir: str, image_file_name: str, ram_addr: int) -> N keystore = KeyStore(KeySourceType.OTP) - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + signature_provider = create_signature_provider(data_dir) mbi = Mbi_PlainSignedRamRtxxx( app=unsigned_img, @@ -458,7 +463,7 @@ def test_ram_signed_otp(data_dir: str, image_file_name: str, ram_addr: int) -> N hmac_key=MASTER_KEY, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ) out_image_file_name = image_file_name.replace("_unsigned.bin", "_signed_otp.bin") @@ -483,7 +488,8 @@ def test_ram_signed_keystore(data_dir: str, image_file_name: str, ram_addr: int) path = os.path.join(data_dir, INPUT_IMAGES_SUBDIR, image_file_name) org_data = load_binary(path) - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + signature_provider = create_signature_provider(data_dir) key_store = get_keystore(data_dir) @@ -496,7 +502,7 @@ def test_ram_signed_keystore(data_dir: str, image_file_name: str, ram_addr: int) trust_zone=TrustZone.disabled(), key_store=key_store, cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=hmac_user_key, ) @@ -521,13 +527,14 @@ def test_xip_signed(data_dir: str, image_file_name: str) -> None: path = os.path.join(data_dir, INPUT_IMAGES_SUBDIR, image_file_name) unsigned_img = load_binary(path) - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + signature_provider = create_signature_provider(data_dir) mbi = Mbi_PlainSignedXipRtxxx( app=unsigned_img, load_addr=0x08001000, cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, ) out_image_file_name = image_file_name.replace("_unsigned.bin", "_signed.bin") @@ -552,7 +559,8 @@ def test_ram_encrypted_otp(data_dir: str, image_file_name: str, ram_addr: int) - org_data = load_binary(path) key_store = KeyStore(KeySourceType.OTP) - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + signature_provider = create_signature_provider(data_dir) with open(os.path.join(data_dir, KEYSTORE_SUBDIR, "userkey.txt"), "r") as f: hmac_user_key = f.readline() @@ -562,7 +570,7 @@ def test_ram_encrypted_otp(data_dir: str, image_file_name: str, ram_addr: int) - load_addr=ram_addr, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=hmac_user_key, key_store=key_store, ctr_init_vector=ENCR_CTR_IV, @@ -592,7 +600,8 @@ def test_ram_encrypted_keystore(data_dir: str, image_file_name: str, ram_addr: i # load keystore with HMAC user key key_store = get_keystore(data_dir) - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + signature_provider = create_signature_provider(data_dir) with open(os.path.join(data_dir, KEYSTORE_SUBDIR, "userkey.txt"), "r") as f: hmac_user_key = f.readline() @@ -602,7 +611,7 @@ def test_ram_encrypted_keystore(data_dir: str, image_file_name: str, ram_addr: i load_addr=ram_addr, trust_zone=TrustZone.disabled(), cert_block=cert_block, - priv_key_data=priv_key_pem_data, + signature_provider=signature_provider, hmac_key=hmac_user_key, key_store=key_store, ctr_init_vector=ENCR_CTR_IV, @@ -657,7 +666,9 @@ def test_sb_unsigned_keystore(data_dir: str, subdir: str, image_name: str) -> No ) # certificate + private key - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + priv_key_pem_data = load_binary(os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem")) + boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data @@ -740,7 +751,9 @@ def test_sb_unsigned_otp(data_dir: str, subdir: str, image_name: str) -> None: ) # certificate + private key - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + priv_key_pem_data = load_binary(os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem")) + boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data @@ -820,7 +833,9 @@ def test_sb_signed_encr_keystore(data_dir: str, subdir: str, image_name: str) -> ) # certificate + private key - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + priv_key_pem_data = load_binary(os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem")) + boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data @@ -901,7 +916,9 @@ def test_sb_otfad_keystore(data_dir: str, subdir: str, image_name: str, secure: ) # certificate + private key - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + priv_key_pem_data = load_binary(os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem")) + boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data @@ -1033,7 +1050,9 @@ def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) ) # certificate + private key - cert_block, priv_key_pem_data = create_cert_block(data_dir) + cert_block = create_cert_block(data_dir) + priv_key_pem_data = load_binary(os.path.join(data_dir, "keys_certs", "k0_cert0_2048.pem")) + boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data diff --git a/tests/nxpcertgen/data/cert_config_secp256.yaml b/tests/nxpcertgen/data/cert_config_secp256.yaml index c80b256b..174aea5f 100644 --- a/tests/nxpcertgen/data/cert_config_secp256.yaml +++ b/tests/nxpcertgen/data/cert_config_secp256.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # This is template for configuration file used for generating certificates # ============================================== diff --git a/tests/nxpcertgen/data/certgen_config.yaml b/tests/nxpcertgen/data/certgen_config.yaml index 0d615da6..9dd5f011 100644 --- a/tests/nxpcertgen/data/certgen_config.yaml +++ b/tests/nxpcertgen/data/certgen_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + issuer: COMMON_NAME: ONE COUNTRY_NAME: PL diff --git a/tests/nxpdevhsm/data/cfg_sb3_keyblob.yaml b/tests/nxpdevhsm/data/cfg_sb3_keyblob.yaml index ef0c7f8c..4b64f7ef 100644 --- a/tests/nxpdevhsm/data/cfg_sb3_keyblob.yaml +++ b/tests/nxpdevhsm/data/cfg_sb3_keyblob.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + family: lpc55s3x description: LPC55S36 SB3 keyblob 4 commands: diff --git a/tests/nxpdevhsm/data/cfg_sb3_keyblob4.yaml b/tests/nxpdevhsm/data/cfg_sb3_keyblob4.yaml index 10a9b19d..99be15c5 100644 --- a/tests/nxpdevhsm/data/cfg_sb3_keyblob4.yaml +++ b/tests/nxpdevhsm/data/cfg_sb3_keyblob4.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + family: lpc55s3x description: LPC55S36 SB3 keyblob 4 commands: diff --git a/tests/nxpdevhsm/data/cfg_sb3_load.yaml b/tests/nxpdevhsm/data/cfg_sb3_load.yaml index bb1bc053..08f2bb8c 100644 --- a/tests/nxpdevhsm/data/cfg_sb3_load.yaml +++ b/tests/nxpdevhsm/data/cfg_sb3_load.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + family: lpc55s3x description: LPC55S36 SB3 commands: diff --git a/tests/nxpdevhsm/test_nxpdevhsm.py b/tests/nxpdevhsm/test_nxpdevhsm.py index f9deea47..209e0660 100644 --- a/tests/nxpdevhsm/test_nxpdevhsm.py +++ b/tests/nxpdevhsm/test_nxpdevhsm.py @@ -20,7 +20,6 @@ def test_nxpdevhsm_run_generate(data_dir, tmpdir): runner = CliRunner() with use_working_directory(data_dir): - cmd = f"generate -p COMx -f lpc55s3x -k test_bin.bin -o test_bin.bin {tmpdir}/bootable_images/cust_mk_sk.sb" result = runner.invoke(nxpdevhsm.main, cmd.split()) assert result.exit_code == 1 @@ -40,7 +39,7 @@ def test_load_commands(data_dir, config, n_cmds, nf_cmds): with use_working_directory(data_dir): devhsm = DeviceHsm( mboot=None, - user_pck=b"abcd", + cust_mk_sk=b"abcd", oem_share_input=b"abcd", info_print=None, container_conf=config, @@ -76,7 +75,7 @@ def test_load_commands_with_keyblob4(data_dir): with pytest.raises(SPSDKError): devhsm = DeviceHsm( mboot=None, - user_pck=b"abcd", + cust_mk_sk=b"abcd", oem_share_input=b"abcd", info_print=None, container_conf="cfg_sb3_keyblob4.yaml", diff --git a/tests/nxpimage/data/ahab/config_ctcm.json b/tests/nxpimage/data/ahab/config_ctcm.json index cc9fb083..be238583 100644 --- a/tests/nxpimage/data/ahab/config_ctcm.json +++ b/tests/nxpimage/data/ahab/config_ctcm.json @@ -1,5 +1,5 @@ { - "family": "rt1180", + "family": "rt118x", "revision": "a0", "output": "cntr_ctcm_cm33_img.bin", "image_type": "non_xip", diff --git a/tests/nxpimage/data/ahab/config_fspi1.json b/tests/nxpimage/data/ahab/config_fspi1.json index 859a1244..e50a5255 100644 --- a/tests/nxpimage/data/ahab/config_fspi1.json +++ b/tests/nxpimage/data/ahab/config_fspi1.json @@ -1,24 +1,24 @@ { - "family": "rt1180", - "revision": "a0", - "output": "./cntr_fspi1_xip_cm33_img.bin", - "containers": [ - { - "srk_set": "none", - "fuse_version": 0, - "sw_version": 0, - "image_array_entries": [ - { - "image_path": "./bin/fspi1_xip_cm33_img.bin", - "image_offset": "0x2000", - "load_address": "0x38003000", - "entry_point": "0x38003000", - "image_type": "executable", - "core_id": "cortex-m33", - "hash_type": "sha512", - "is_encrypted": false - } - ] - } - ] + "family": "rt118x", + "revision": "a0", + "output": "./cntr_fspi1_xip_cm33_img.bin", + "containers": [ + { + "srk_set": "none", + "fuse_version": 0, + "sw_version": 0, + "image_array_entries": [ + { + "image_path": "./bin/fspi1_xip_cm33_img.bin", + "image_offset": "0x2000", + "load_address": "0x38003000", + "entry_point": "0x38003000", + "image_type": "executable", + "core_id": "cortex-m33", + "hash_type": "sha512", + "is_encrypted": false + } + ] + } + ] } diff --git a/tests/nxpimage/data/ahab/config_fspi2.json b/tests/nxpimage/data/ahab/config_fspi2.json index fa2f456a..03336aad 100644 --- a/tests/nxpimage/data/ahab/config_fspi2.json +++ b/tests/nxpimage/data/ahab/config_fspi2.json @@ -1,24 +1,24 @@ { - "family": "rt1180", - "revision": "a0", - "output": "./cntr_ele_fw_fspi2_xip_cm33_img.bin", - "containers": [ - { - "srk_set": "none", - "fuse_version": 0, - "sw_version": 0, - "image_array_entries": [ - { - "image_path": "./bin/fspi2_xip_cm33_img.bin", - "image_offset": "0x2000", - "load_address": "0x14003000", - "entry_point": "0x14003000", - "image_type": "executable", - "core_id": "cortex-m33", - "hash_type": "sha512", - "is_encrypted": false - } - ] - } - ] + "family": "rt118x", + "revision": "a0", + "output": "./cntr_ele_fw_fspi2_xip_cm33_img.bin", + "containers": [ + { + "srk_set": "none", + "fuse_version": 0, + "sw_version": 0, + "image_array_entries": [ + { + "image_path": "./bin/fspi2_xip_cm33_img.bin", + "image_offset": "0x2000", + "load_address": "0x14003000", + "entry_point": "0x14003000", + "image_type": "executable", + "core_id": "cortex-m33", + "hash_type": "sha512", + "is_encrypted": false + } + ] + } + ] } diff --git a/tests/nxpimage/data/ahab/config_fspi2eleFw.json b/tests/nxpimage/data/ahab/config_fspi2eleFw.json index 942e9cfd..c52d36f4 100644 --- a/tests/nxpimage/data/ahab/config_fspi2eleFw.json +++ b/tests/nxpimage/data/ahab/config_fspi2eleFw.json @@ -1,27 +1,27 @@ { - "family": "rt1180", - "revision": "a0", - "output": "./cntr_ele_fw_fspi2_xip_cm33_img.bin", - "containers": [ - { - "path": "./bin/mxrt1180a0-ahab-container.img" - }, - { - "srk_set": "none", - "fuse_version": 0, - "sw_version": 0, - "image_array_entries": [ - { - "image_path": "./bin/fspi2_xip_cm33_img.bin", - "image_offset": "0x3000", - "load_address": "0x14004000", - "entry_point": "0x14004000", - "image_type": "executable", - "core_id": "cortex-m33", - "hash_type": "sha512", - "is_encrypted": false - } - ] - } - ] + "family": "rt118x", + "revision": "a0", + "output": "./cntr_ele_fw_fspi2_xip_cm33_img.bin", + "containers": [ + { + "path": "./bin/mxrt1180a0-ahab-container.img" + }, + { + "srk_set": "none", + "fuse_version": 0, + "sw_version": 0, + "image_array_entries": [ + { + "image_path": "./bin/fspi2_xip_cm33_img.bin", + "image_offset": "0x3000", + "load_address": "0x14004000", + "entry_point": "0x14004000", + "image_type": "executable", + "core_id": "cortex-m33", + "hash_type": "sha512", + "is_encrypted": false + } + ] + } + ] } diff --git a/tests/nxpimage/data/ahab/ctcm_cm33_encrypted_img.yaml b/tests/nxpimage/data/ahab/ctcm_cm33_encrypted_img.yaml index 5039697d..676cd42c 100644 --- a/tests/nxpimage/data/ahab/ctcm_cm33_encrypted_img.yaml +++ b/tests/nxpimage/data/ahab/ctcm_cm33_encrypted_img.yaml @@ -1,9 +1,12 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause --- # ---------------------------------------------------------------------------------------------------- -# =========== Advanced High-Assurance Boot Configuration template for rt1180. =========== +# =========== Advanced High-Assurance Boot Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == -family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] +family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] image_type: non_xip # [Required], Type of image, The final use of image, this setting is changing the style of offsets in final container., Possible options:['xip', 'non_xip', 'serial_downloader'] output: ahab/cntr_encrypted_ctcm_cm33.bin # [Required], Output AHAB file name, Revision of silicon diff --git a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert.yaml b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert.yaml index a72d93f7..964188fc 100644 --- a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert.yaml +++ b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert.yaml @@ -1,9 +1,12 @@ ---- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # ---------------------------------------------------------------------------------------------------- -# =========== Advanced High-Assurance Boot Configuration template for rt1180. =========== +# =========== Advanced High-Assurance Boot Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == -family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] +family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] image_type: xip # [Required], Type of image, The final use of image, this setting is changing the style of offsets in final container., Possible options:['xip', 'non_xip', 'serial_downloader'] output: ahab/cntr_signed_ctcm_cm33_cert.bin # [Required], Output AHAB file name, Revision of silicon diff --git a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_nx.yaml b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_nx.yaml index 68bdf9fc..9106c2e2 100644 --- a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_nx.yaml +++ b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_nx.yaml @@ -1,9 +1,12 @@ ---- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # ---------------------------------------------------------------------------------------------------- -# =========== Advanced High-Assurance Boot Configuration template for rt1180. =========== +# =========== Advanced High-Assurance Boot Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == -family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] +family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] image_type: non_xip # [Required], Type of image, The final use of image, this setting is changing the style of offsets in final container., Possible options:['xip', 'non_xip', 'serial_downloader'] output: ahab/cntr_signed_ctcm_cm33_cert_nx.bin # [Required], Output AHAB file name, Revision of silicon diff --git a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_sb.yaml b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_sb.yaml index 91381d85..34428b54 100644 --- a/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_sb.yaml +++ b/tests/nxpimage/data/ahab/ctcm_cm33_signed_cert_sb.yaml @@ -1,9 +1,12 @@ ---- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # ---------------------------------------------------------------------------------------------------- -# =========== Advanced High-Assurance Boot Configuration template for rt1180. =========== +# =========== Advanced High-Assurance Boot Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == -family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] +family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] image_type: serial_downloader # [Required], Type of image, The final use of image, this setting is changing the style of offsets in final container., Possible options:['xip', 'non_xip', 'serial_downloader'] output: ahab/cntr_signed_ctcm_cm33_cert_sb.bin # [Required], Output AHAB file name, Revision of silicon diff --git a/tests/nxpimage/data/ahab/ctcm_cm33_signed_img.json b/tests/nxpimage/data/ahab/ctcm_cm33_signed_img.json index 8d483190..24db388f 100644 --- a/tests/nxpimage/data/ahab/ctcm_cm33_signed_img.json +++ b/tests/nxpimage/data/ahab/ctcm_cm33_signed_img.json @@ -1,5 +1,5 @@ { - "family": "rt1180", + "family": "rt118x", "revision": "a0", "output": "ahab/cntr_signed_ctcm_cm33_img.bin", "image_type": "non_xip", diff --git a/tests/nxpimage/data/ahab/return_lc.yaml b/tests/nxpimage/data/ahab/return_lc.yaml index 7e4ea2c8..2eef07a6 100644 --- a/tests/nxpimage/data/ahab/return_lc.yaml +++ b/tests/nxpimage/data/ahab/return_lc.yaml @@ -1,8 +1,12 @@ -# =========== Signed message Configuration template for rt1180. =========== +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# =========== Signed message Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == # ---------------------------------------------------------------------------------------------------- -family: rt1180 # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt1180'] +family: rt118x # [Required], MCU family, Family identifier including the chip revision. If revision is not present, latest revision is used as default., Possible options:['rt118x'] revision: a0 # [Optional], MCU revision, Revision of silicon, Possible options:['a0'] output: ahab/signed_msg_oem_field_return.bin # [Required], Output file name, Output Signed Message file name # ---------------------------------------------------------------------------------------------------- diff --git a/tests/nxpimage/data/bee/both_engines_ctr/bee_config.yaml b/tests/nxpimage/data/bee/both_engines_ctr/bee_config.yaml index 0aca47c1..1a28a249 100644 --- a/tests/nxpimage/data/bee/both_engines_ctr/bee_config.yaml +++ b/tests/nxpimage/data/bee/both_engines_ctr/bee_config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== BEE configuration template =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == diff --git a/tests/nxpimage/data/bee/both_engines_generated_header/bee_config.yaml b/tests/nxpimage/data/bee/both_engines_generated_header/bee_config.yaml index cdba8393..916c22b4 100644 --- a/tests/nxpimage/data/bee/both_engines_generated_header/bee_config.yaml +++ b/tests/nxpimage/data/bee/both_engines_generated_header/bee_config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== BEE configuration template =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == diff --git a/tests/nxpimage/data/bee/both_engines_generated_header_overlap/bee_config.yaml b/tests/nxpimage/data/bee/both_engines_generated_header_overlap/bee_config.yaml index 1427c53d..4f892107 100644 --- a/tests/nxpimage/data/bee/both_engines_generated_header_overlap/bee_config.yaml +++ b/tests/nxpimage/data/bee/both_engines_generated_header_overlap/bee_config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== BEE configuration template =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == diff --git a/tests/nxpimage/data/bee/one_engine_generated_header/bee_config.yaml b/tests/nxpimage/data/bee/one_engine_generated_header/bee_config.yaml index 16851498..8d896d36 100644 --- a/tests/nxpimage/data/bee/one_engine_generated_header/bee_config.yaml +++ b/tests/nxpimage/data/bee/one_engine_generated_header/bee_config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== BEE configuration template =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == diff --git a/tests/nxpimage/data/bootable_image/lpc55s3x/application.bin b/tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/application.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/lpc55s3x/application.bin rename to tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/application.bin diff --git a/tests/nxpimage/data/bootable_image/lpc55s3x/config.yaml b/tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/config.yaml similarity index 76% rename from tests/nxpimage/data/bootable_image/lpc55s3x/config.yaml rename to tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/config.yaml index 094d6d50..9588b578 100644 --- a/tests/nxpimage/data/bootable_image/lpc55s3x/config.yaml +++ b/tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== Bootable Image Configuration template for lpc55s3x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == @@ -8,6 +12,6 @@ memory_type: flexspi_nor # [Required], Memory type., Specify type of memory used # ---------------------------------------------------------------------------------------------------- # == Bootable Image blocks definition == # ---------------------------------------------------------------------------------------------------- -fcb: ./bootable_image/lpc55s3x/fcb.bin # [Optional], FCB block path, Flash Configuration block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path image_version: 0x1258 # [Optional], FCB block path, Flash Configuration block path -application: ./bootable_image/lpc55s3x/application.bin # [Optional], Application, Application image path +application: application.bin # [Optional], Application, Application image path diff --git a/tests/nxpimage/data/bootable_image/lpc55s3x/fcb.bin b/tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/fcb.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/lpc55s3x/fcb.bin rename to tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/fcb.bin diff --git a/tests/nxpimage/data/bootable_image/lpc55s3x/merged_image.bin b/tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/merged_image.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/lpc55s3x/merged_image.bin rename to tests/nxpimage/data/bootable_image/lpc55s3x/flexspi_nor/merged_image.bin diff --git a/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/config.yaml new file mode 100644 index 00000000..4351efff --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/config.yaml @@ -0,0 +1,13 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt101x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt101x # [Required], MCU family, MCU family name., Possible options:['rt101x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +keyblob: keyblob.bin # [Optional], Key Blob block path, Key blob block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt117x/fcb.bin b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/fcb.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt117x/fcb.bin rename to tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/fcb.bin diff --git a/tests/nxpimage/data/bootable_image/rt117x/application.bin b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/hab_container.bin similarity index 81% rename from tests/nxpimage/data/bootable_image/rt117x/application.bin rename to tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/hab_container.bin index 60b121d520ed83729c55a5037d70ecf9768ff9a9..2d908b84795aa1d4fb41af87b4307ff05a39ff68 100644 GIT binary patch delta 4138 zcmeIuu?c`M6a>&8dm$ragsfm=Cu9X1*T@1cVQ=9GR@QMN6@);VtFF5bfqZgY;FZ;t z-R0ZDFwGY2pWxs7ck0Bn)TO=k=FiR9W#+7FAMu`XgqqO*R9;vve@H|P$e+p!%jFM= nr~&y?d11NyArUnoe=095mp>$;2INoWh2`>xMASe)?ME6POWPU$ delta 10 RcmcbxhVeoV<0gRsHvk(M1Tz2t diff --git a/tests/nxpimage/data/bootable_image/rt5xx/keyblob.bin b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/keyblob.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt5xx/keyblob.bin rename to tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/keyblob.bin diff --git a/tests/nxpimage/data/bootable_image/rt117x/merged_image.bin b/tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/merged_image.bin similarity index 98% rename from tests/nxpimage/data/bootable_image/rt117x/merged_image.bin rename to tests/nxpimage/data/bootable_image/rt101x/flexspi_nor/merged_image.bin index cb4ed984444bbadd6975f0fb3a8cd022efc51f5c..1ce2c92bdf0f44af48e7863cb7cd880c5a3f978d 100644 GIT binary patch delta 272 zcmV+r0q_3M%mL8M0e=7i0s{mE1_uZU3JVMk4i69!5)%{^78e*98XFuP9v>hfA|oUv zCMPHu$;>FVq3?e6dJ@$&QZ W_4fDp`TG0({Q~~~p#cN31W;oC!G*{G delta 14 Wcmca`hVjA~#>Ed+y diff --git a/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/bee_header_0.bin b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/bee_header_0.bin new file mode 100644 index 0000000000000000000000000000000000000000..70890f713c2d3fe2969b91d0f0ed82e2b9c50f89 GIT binary patch literal 512 zcmdn<=|hhB*_o^}U7d4YExJBC=5n@X<_5W0wa>qAJQcR0m4U`!)~p2rsd1kpwmoTZ znx%2e`QzJO=ca$QhQen3A)E~?@qbx@Cam{c`bv1J$Tyx}JmptoVhXumy!Ti~;WoresxwD%-1x)n1^UeOU z3_F`w(}_j%0*bAa_f-8@G(T(g2@#{0pO5-pcxC^qvf0zQKawjwZ_(N6opwqun&0?5 ze7T`vZLDd$woh2Xke$3^)6H2kBhtHj;u}|yK)P^*{-nf$JWgd z<$c^ZQ8@GTm+(6i+zb+r8=9qTJYbkN(^_V+&9c3F7jZ>PKNi?{$m7PQx~p#z%$wYc zjygP^6qf;tM)#OG-C#W%ai zzuW!3;^3CM=UyCNC%S68OHb6wbS8-_ULi%RgqEra9sBex=H~CajIlZ!9vl3+b%Msh HOVD@#rKyo7 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/config.yaml new file mode 100644 index 00000000..e089d479 --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/config.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt106x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt105x # [Required], MCU family, MCU family name., Possible options:['rt102x', 'rt105x', 'rt106x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +bee_header_0: bee_header_0.bin # [Optional], BEE encryption header 0, BEE encryption header 0 path +bee_header_1: bee_header_1.bin # [Optional], BEE encryption header 1, BEE encryption header 1 path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/fcb.bin b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..616b6d5a4efb760f4b2e191a792f45e8c900da5d GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0!C(L5JeIY0IFtWVMo%_fW#)ue9a=k#b(5!22;t(!lD9WW24zv xu!$nX6j&rU5In-H8))j8fP5AO-1-s-*pFK$jbs>s<^m%DL_q+81rxzX0{~9@1-}3Q literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/hab_container.bin b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..f462d00749bcdd9328dfbd8dcdc31c0b74b53ab4 GIT binary patch literal 9606 zcmeHM4{%&n)j#*Wm+WSnCU4Vi-GnrGo2Ig9%ecW-i-O$EhRr6l-4J1h5gcEN&=P$BCd%ttfJ@?#u&%5{ByPIJ$qGXUsb00&8EOF@r*}ha-lolhf7=gtI zEJk240*et?jKE?979+42fyD?cMqn`lixKz_i~uwI?i!12+L51#SoK0zL_R2KcO$;cFgWMEPOh zo4{khkAZ$*0C)~~0T=;Z0sa?w6L=fA02IN`QegQ4fAfC-QCJ#sV=uPBB`W8`h7d zT2g&V{7{)1zs#pPwJJG6(_0mlw5#UVL$KYS{s6`3$JVQVvejVx(J>009vjUc4S3B| z%V_=ymy=WWduh$_?2@QDL^%JV9II5z+Z)GfM zmSj8^wVsPw84ta0zcrd~Uti51C}xh`8HzyRY}^Zm&w;vzR7M)T`5W<{s$ zMrJZAG(}*db1&Gw?|b=SL_sntxdpxh*0&)sqsr`@s9bpA!7c3Pa>RfpRg*cMBDk zPs8#mA=?~4dw)E^a1$$t|@W{3e~Xqe4!N2{m>mmA2#|>A|kg(_6^<*u4)&#UEngbNx41NyIy3( z4M-KT4mMlezI15T6+Eabg>?CBBq50l@C>V@Yn5jrSLs?gkOHpN8<5_(Aicf@Y3xGz z8M(UOGksY|@>izykX+-v(-k2Fd=RXHd#1^`VR|&*cMq$>)4vfb{PF^JE_3%vbZ*Zu zW^TNjPf`2EZRKZ<>Ev89E#L7$^z6c*V7XU*@b`Z8Fw%vw8PA* zEOZAnQzkU3s|iGBHFnC7HI9uMmr)+gua8olKW@M$=2fSLq%;Pp*p*&|rI^(hH{{)VZUGIP!VS737ZnA?jz{jD#7rcY)@E-x# zSKmqYI>_s!4Bz3ifCCve8?zpJDg1+t&eD$LaPLRw4@;3=jF4=edGg#C~T<$41UXr2Qq<33X-!JH9b$ zDprMdex%#Y;D(>V%CK(8e6AY_SEpfZL>-1TA#=PI@*}JAsCwTcsyfAE$lLJZHuKgQ*r- zdrv~{Gxuz|yVcuDY}=*!js5$Nl0m&)n+(fV%SPsF40mN4CKD2$`8C-8K465UNSq>-gI_LMCnxzr?V%B^(u#BXEQsM30}btH%-_(+qQyh z*dYo}4l9rpNaMrv?UB<(bE{k#&4~ms$8A0{Va8C3K^s7A6L>i|3XZz9QPjGnIc6DN zO}*D9=<7qAh3RnaMGBV>U7g@1oN=d;ye1vyHJr%#5OJp|CF7YeH=y+f_|%YpuNQLm zb*JWM|A!sNvsCR<%bktkzM0ur$u}dT zU2$rDo@Ds`Pn3}b=i(}Di;hMHi0a>@OaN#0|ZU414?8uE%4~cIpkvNt!HGl?+W{ZdY~GXloi?9396#_ARSfO;wg!pP>ExJJ^#e zGYP7+)Vh8Aq1b+28ml$~3p7fdf*YGR7R2pvzEvGsDVgJ4OJmP9x%sA4r#r3g{O{`E!TjY_mEx7oA=f7aqz8Rnea=P zLdvPNiS8RyEo%~rQ?BUEl-H_LEgP{aK3>x|Y47!&T)la_SsqF{A|i5s#QpdOrpE}I zvd)gCW9jlM2UfOvku#McWV@l_gVpqJ%##|$h{(#Btpp4lf%rd`(1&d&KaZEC=1bUKZ&!sdT zu!C;7$}KCr%C1c))*($9scCC6i!z(e7#&UK=Ck37CNp_f?A(V_u+FiSCdW_|a|LWq zdejlc>hN}ug8AC&@>BA;Fi(`V=@+xR%=WY(+xw4^Q}(pX5o9mnM7U$5mvG~`@bBf^ zWZp=%lw}kvW+~0y$j2t0-P1gHee*3f9eM57F`%S&dbmBWT}BOWAkKFGsWZ_(jc>;k<8ce8=2nkX}z~O*jT)2PxFfPTE=I2SNfZKT3Gx@Q+`+4 z3D|PB=Z4>&Atv8Y3TMlFo=-X~^@q1~pUho1;d?mN8OeJgh_)4kfJ`qlS(?>h9TPLs zak=Y7E-ikV#of!0Pkj3;kVAQL*wckEPkbZtrjkE5Rv0rvCF;8ygT~{(m>w%iu(;Z1 z#qDfK&uptFv1@)O9((ic0%3mgw}XENUVY{4INm?Ut#f?K9N#s^d*}F}IsWb(|GUiN UuZ=_G3WNORpZN=TN3(x_20z*G+yDRo literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/merged_image.bin b/tests/nxpimage/data/bootable_image/rt102x/flexspi_nor/merged_image.bin new file mode 100644 index 0000000000000000000000000000000000000000..2e46fb4a0329942105a74ebb29e0f3abde3690c0 GIT binary patch literal 13702 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 XWgh?Cb4VUvHGkoE;Xb^hu77_AQb8O| literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt105x/application.bin b/tests/nxpimage/data/bootable_image/rt105x/application.bin deleted file mode 100644 index 36a0b49ec3980d052c0e2e62d2f41c3d62bdadf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12808 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 HOVD@#rKyo7 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/config.yaml new file mode 100644 index 00000000..e089d479 --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/config.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt106x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt105x # [Required], MCU family, MCU family name., Possible options:['rt102x', 'rt105x', 'rt106x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +bee_header_0: bee_header_0.bin # [Optional], BEE encryption header 0, BEE encryption header 0 path +bee_header_1: bee_header_1.bin # [Optional], BEE encryption header 1, BEE encryption header 1 path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/fcb.bin b/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..5b67c6d8b4b7b7c5be0a4824c4bc3e7274177f79 GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0%jlp(WHP#pkhW2c19#UEJ$peOn#sYKLaDff_8~+fqtINEX$!v z+Zljx6%exmaX~voo_jlXd5}5~3V*-Xe3oDT31k&6n()c+{AZZo`MxYsxNFXZs;bsB= Dogzux literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/hab_container.bin b/tests/nxpimage/data/bootable_image/rt105x/flexspi_nor/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..bad474c09a1b98e2fd919927978b9a2228c6a015 GIT binary patch literal 10872 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 QZ-IO1nN{f#^cDO41^a`GaR2}S literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt105x/ivt.bin b/tests/nxpimage/data/bootable_image/rt105x/ivt.bin deleted file mode 100644 index 7cbc7d026534a68af23b3546dd57a0dd92479c70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 Ycmcb}py0r&%#gqU1quQ{HV8uj06x6}x&QzG diff --git a/tests/nxpimage/data/bootable_image/rt105x/merged_image.bin b/tests/nxpimage/data/bootable_image/rt105x/merged_image.bin deleted file mode 100644 index 8868867a526a69e35cf614fbe82f9ca72e23d627..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21000 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 diff --git a/tests/nxpimage/data/bootable_image/rt106x/application.bin b/tests/nxpimage/data/bootable_image/rt106x/application.bin deleted file mode 100644 index 36a0b49ec3980d052c0e2e62d2f41c3d62bdadf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12808 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 HOVD@#rKyo7 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/config.yaml new file mode 100644 index 00000000..3326ace9 --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/config.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt106x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt106x # [Required], MCU family, MCU family name., Possible options:['rt102x', 'rt105x', 'rt106x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +bee_header_0: bee_header_0.bin # [Optional], BEE encryption header 0, BEE encryption header 0 path +bee_header_1: bee_header_1.bin # [Optional], BEE encryption header 1, BEE encryption header 1 path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/fcb.bin b/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..616b6d5a4efb760f4b2e191a792f45e8c900da5d GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0!C(L5JeIY0IFtWVMo%_fW#)ue9a=k#b(5!22;t(!lD9WW24zv xu!$nX6j&rU5In-H8))j8fP5AO-1-s-*pFK$jbs>s<^m%DL_q+81rxzX0{~9@1-}3Q literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/hab_container.bin b/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..2c32bdc53d6261499c84c56502b9d1b29fcdab5c GIT binary patch literal 11060 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 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/merged_image.bin b/tests/nxpimage/data/bootable_image/rt106x/flexspi_nor/merged_image.bin new file mode 100644 index 0000000000000000000000000000000000000000..a05d5300454b6a2b370e70dd669d320e5a2e2c95 GIT binary patch literal 15156 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 BPuu_i literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt106x/ivt.bin b/tests/nxpimage/data/bootable_image/rt106x/ivt.bin deleted file mode 100644 index 7cbc7d026534a68af23b3546dd57a0dd92479c70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 Ycmcb}py0r&%#gqU1quQ{HV8uj06x6}x&QzG diff --git a/tests/nxpimage/data/bootable_image/rt106x/merged_image.bin b/tests/nxpimage/data/bootable_image/rt106x/merged_image.bin deleted file mode 100644 index 8868867a526a69e35cf614fbe82f9ca72e23d627..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21000 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 diff --git a/tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/config.yaml b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/config.yaml new file mode 100644 index 00000000..779a6c6d --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/config.yaml @@ -0,0 +1,11 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration for rt117x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt117x # [Required], MCU family name; Possible options:['rt116x', 'rt117x'] +revision: latest # [Optional], Chip silicon revision; If needed this could be used to specify silicon revision of device; Possible options:['latest'] +memory_type: flexspi_nand # [Required], Memory type; Specify type of memory used by bootable image description; Possible options:['flexspi_nor', 'flexspi_nand', 'semc_nand'] +hab_container: hab_container.bin # [Required], HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/hab_container.bin b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nand/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..a720a4ef3a1ffe2a7f6bc78cad56ee92b0f60601 GIT binary patch literal 22492 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 t|HX5_O275lwMxW{{m&*$^~ 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 G3jQZ-7w|{` literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/config.yaml new file mode 100644 index 00000000..5103682b --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/config.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt117x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt116x # [Required], MCU family, MCU family name., Possible options:['rt116x', 'rt117x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest', 'latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +keyblob: keyblob.bin # [Optional], Key Blob block path, Key blob block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +keystore: keystore.bin # [Optional], Key Store block path, Key store block path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/fcb.bin b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..cf32c5247fb814c111071e2edc955ea4893bd0b4 GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0!C(L5JeIY0IFtWVMo@(h|D3xe$67m#b(5!22;n%!lD9WW24zv uu!$nX6j&rU5In-H8))j8fP5AO-1-s-*pFK$jbuQf4U7atpd(ns*a!f5nFT-q literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/hab_container.bin b/tests/nxpimage/data/bootable_image/rt116x/flexspi_nor/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..2d908b84795aa1d4fb41af87b4307ff05a39ff68 GIT binary patch literal 22096 zcmeHv4R}*ky6(4kk|zD3Nq;DX7T5_C6D*Kc)S`$xY1t$Q7Ahzz4oNAT6blV4dI0g5 z`ZKr8=vdG3&P9irsm|OPyv|QiI)_m@E&j~tjN_94&fqwn#xq`9i#oe)B%v+Ieb-LX zpQv-MbM8FPJm=7dZ+~mO>-)a-zxG<++95!KZLOQEl=u@22s4`av##4d5lIbABSX(q z9Q~4rBR)0pQv6a2Txx+!EpVv?F15g=7P!;`ms;Rb3tVb}OD%Az1unI~r55;mX@LjW z`Tyk#vNm2v)*phN1)Z*(;sa31df)A2{oe^+fxeckMWDC0lJz~%hoC{w1rS$9)^tz~ zXeP)AvVh7!%Rn}e6SN+5D+_O*AUERmpxvN-pcc?U&|{z{Ku>|50UZVX9CQ-&Iw$~o z8}uINL(m}T0*Kp&vVd|xGeJg>1ylxF2C{*ij3$-gZxvpTd~OA~LG_^Bpnaeg&_U2& zo9|_89?$Lp@gG z-q%%JsHd#eo()7)tIpsV@`+{_I4yyOAhVdat2Z*HA$j-3#hwK+-CPjrsaE?JgnA@N z_DPFfE{|(inMe?pDA1v9)av#)TWtemvxUgk8#%1uT)hz?&5UQhWRzIl{)Ai%K#bA|Vt+(T zhgiJ6EJr504o@pbZN4qcgxk&xdT@uTEF8DqU{ zm9os~f2a<9tEo5AfRqzRc}ht+KFI4;{^Nrxy~dv&?2XJ{*&AuUYZ^lBN=O&14m}O6 zrTI^^ll>1EMp5$GY5Bt0Nhg%|@X=oR^Y`MgnGyN)ay1zQ~ zGOK+a+8DJrDK+62^M2Lh41XT#TdG!jt~auLvqX%xJ-NURPr-tFSZn+SR?K-fqcupe z@iBcQW|gL&5{JH(i%|mmC)6Yljk0#1Uz&cpI`k$qLP&cY`F^0_RI2g+>u5-(b3L4c zhA;z%x+*$*Bdt|}$H!;Cg(t?OfO$mg1Q$3h1jgo(;3~sBI>wXy+fmFX73Xow_m#1+ zv`#JH`PxkuWh`}#^+rS>@z~E0t3V8PF?V_+Rz>!44YTI!)R?b1+uM)c%$Yu~>`Jo0 z7Ed1DkKCx;UY$UoEZ(GMTdp8J{BKgUl<63`^@##q}rBew4@bc}0It zP^Iu$L2;-AYt7jl0j+tf(lT5ZyVS@a9{oi%6eJT_IKSFRL7mYa^4Uqg&^3WnhkWHE z%Tp1wP2Us*cI1CU`>HV_IAbh4h?TNE*DR5|aEy$C)YZRK{Gd;{Oc~D$xg)Qar`iGX zLRSQKvZWc)Y>6}TeuBLtG3_D32(!0Rt9^j%cIX;lm*nqVOncI*guZj}8c9$>Ph2E{ z+U}cIE^%0&Nq*v@082XmOrX`WYwJRZm5Jm>E^^?p@M9=#D5nl9!s$_lboGbCU43N6 zoYZh0RlpeX**KSXl`X+pN^v5E*2h!g+}BA$Ed1vfhi5MqaUT591eX&0pA^n|d4rP2 zAJt!!$191Hsd*5!-%Qs0JJ`PT4RH^6+gA{_zb2CSns}g7BhiB1$itW+J4bpWK8$yg z1)*b5SV94A0cTX9O!^ot&hfLd{zfV5izpMrmnFCyjmOnW>d#~51B9OF$Er&K61cV_ z9%{RD?+%Y;FVng#_MW00i^fNPJdeB>v;PgnVpsnG@yrKo^X$jISVUP}th{K)k1)SJ zr-jMO(ghzenb+bnGVNB{I>Cuzz4ygf`jJGs&mB*XbF@<;kJdyUsqIL=e)3u!k*L%8 zL>++ypLiI1`D37U(6xdDP`p?Fpw#DImFFu;yL=aQV?F&Hv=-wV6Z{c{vmTCxxBh|% zb}8S5KKlONxm0nu_zl=Mz^(y#8*~D=AQlSX8247A8sl*Jm=J1r8Ho?zJ?~Woxl)dW zT)dLr)o&0gB>Gd?qK*h~`u76T@xCKd~W|%RKQ(Po@Eux;|2Wy}avvR^LvVuQ4x-uYv_Li|5Yo zjYK%k!^ts@JUzoFvATU^#P-9ixMx0;T1y zMM{p+a#tag6^wKE=y4v2zCW9U=xN^J&t3$5=tumnIMDnun7G!bGT%|2y~uf*KNpX*O|+t#r#)+M%1^FaleB%Wf(kOTVr8XDgs>}Jmz23PE|a3^N}yzoYtll=PZpJ*w=D9{IPG2KkF1-{ z`qn1%To1zjh-TjS{C{P4h@@8D)D9yqk=V*wasQXCJhu zoqpAn?o4;Y!tX}oE1^YFnV9UIqatTXs2Si)yzAQz6^Qe+C^!j*6UrW8ip+l>Pk-D&o{#&; z@-NOA{w$9=M0<1|A>^~_TsTL;BAGH=kHDhNYE}z zRrU~M??o;zAeUnQfi*@C7oy$R)k2HyOyjwjTz4ZEys43kf+CkzXeQ;J?Av+6OhvO$ z$*<5=n9!Vs5YtkSgjOB$yI;v~>lfyC$6w5^wr|6i$nOT^w;K5s`d5EpepR2F-{QXO zzC=Dtkxv2gxx%UQ=>4n(C->757Bink&5;_7 zbyvS^`}e-=An&R4@KrkNmVuWKRCV7_b^Dx}i265`p!B&H0_!5`BC>ph`8v0gTgdr2 zVOG$xhBKM=(7$kxk@3e7ogqhhB$8oxB$8=h@hpV0sb1N;heqZ)+R;Wq7YXVPLs#}O z>g1jwnUCBNF^pzN19u4{Z4V9J{XMHHJYsQ$YiDt%J^T(@qs4nn zb(l=kC5mvjr+dhv4_J+ymlI|L-6edmghu_glKR4oWz(cGMW&{hEIR+u(c$x|(Sn=| z%g3jzWz!_N&meiRmMr=(QvHn%pQM*yasC3d2iNvUL`SMNb+Bg@cStPe+U_f^*h^-u zzr_40+JH;1mMLSslpf>`={r!OM>EcSji+;VyfE*VbTlKv>C*!3gH}Fc;o#}}<~XHL z-(qtH+tZUSfYiCxWf_(TQXJB!efQ1z9}%wO#lbp#k^jZPdVL|D+x64&l=QiH?$zt@ z+@#OKvr(UpXRSUBPcQr(FAmKNE#B@}Oj$s~|REo};Nji;s9dv5pTVeS!{k!y6Lf@-r znrQ)QQ={iqM@ViO!5+Ac$xee*W1^&)ibj?{JE#*_+h$-V$9P)Mpm08zq43{EsO{Sk zoi$m8jPWuc#m7BcgB}&I2Rj#~(yRRf`edm;KfM8`P0uM*CnUq z&gVxSz<#nN^us}Y_H{)!iEj=HY5Yj`(6H?$v2#$bmCovymsZ)tSAe&RgEpI(9Q#j? zrmdu+XKehbyXW`{Z?*XbINmstJzNWIGTtUyCArIb4ANt&(c+w^N3#@Z)u|s1a#>8? zdV-fSB^P#p-c`>H`tI9yTJC&q8eUbLV8sTVu<&WAd( zBr5S>3_ddge`=es|B;BATzG1!eSgx=_(&v&f|Fu6z1}mCmO-vbu}tDHd$Q?~h@MQI zdTE{Gd-tr1%)?n?+Xc4T#=>1OjbxTI9oB^Rh)T7A>QG^m?h2t66H)5_Zjjg6DofSxXS4m@UtXTMA95=bZ zGxr$1d=y=bZ0iOhsG_d z$1N`&K~HrN1#x~_3V+WAoR?gn_YppT^H2lAdqK6&e_gBd*UZU@aM$83bS=sAeP7p} z4?Lh1Ww;@_4cBN-V;=Qa7!236<9Xqf^~6ou`Q1nRb%u6ORrmUc`dSfm3TRn`zgz%S zb!YBXVT|O?opJhhN)Ip@;MW#q<<4dN{He?!!&r_*8OanD8$MOhojbc`&iw9Vi6Upc ztD=20$f;ViYQG`E72U6GIXV*HJx>pEWsg&IND_(=&_d zF)|$MjU1StDXC;mh0`*Zf2&&N)Zeq``zNrx^~rk;mBgQ`%qyyA(pgemG8ABYbhSm;4%?#RPqC-8QG_fI$ssfWkD3M?+$IXX55?cfJ1e|PEwiP>RxYgh88Yo$Xt zb>CiGDD^yNH2?baG)eH(F+T!4>LRt-d3fWEw>%cKVoq&`|J0DD+Z+qu9Aoi~v9fv* zdNdc_rBk?#DT9BN=fo`|t&t;xoUVF6=h4ZLO^yLh_v7bY9nR9M9MI{088P^*^@2?6 ztnQ=rXb)%z`?$*BJ$T)Of-bJI{T?4y4mEL=u6>+Y*r&y92ai)3xzmEk#jOgGne*FZ z19#L7OYrQ!x6g^2Gg_~!w7OmUjr(o59U83d*dfZT)WLX26gz5rBb)i%J8JLA<#^e^ z=Tw^RsX<6UXm{;X`#Fm$uzQD1)^WRQ-y4r{h&{EBvv7glh+4I~URUXLchnmXFobyl z+=-A+=x|rCopKAjdzd-mP#XI675Jpy;G*HSn6kc^@)dvd!3)))_i$1pLD=ou-M%}x zTWE4MwKoNuT+KprdvmZ^*yGw`=;9n#$$<>K$+-Bj9?(y6#F<(~c zwf>eJ#*nwnq~=3nc}`_Zt*xGu_pUcq+T5}5zs1yc+&&D=T4%t`h+$$ST$FHAl#s!(z`rnR}67FDKWL^<)U@RTP zKFg{yo{?3$w{RCx#;~u!>sx}=`TKPp3Q7vfe8IDk|J)5;)f^^kWU_mJP6s+?Lq2Yn zNWpdhgJ*SU_J%BY4rgshSDt74SU9LWsq_Uum16g}=~mQBH~be!~5IF?SqtPZYzsbk3$>~e>#-s)I51zX@~sV{YuOu}%4=@c9% z>o0Q%lTcr0k%QYY&rvi5D{$C$%ykq@!EzieJBl1RQ?M+@$sIEsS(7ldNzdG%uQScT z-I?pqOiFpVCw5YW`Y%O6JRs`PitvcTCkXO^dO(zpILHU;0Z|6xFCqStSUapY)eg_K zQzv!j(Kn^XCBrgvcYA- zq&xjqB{p%VuaUCYj6C1S_eQ)kLFv)(ui|(1*uQgrO2h4#;NF|gbD^O7>Af{`csYhu zfz7$)Ge71Wtc0aAlV1o#!*3+M5IC9gg#aNp9dc`%_(A~p7-Krzg<$>S z6;CRAA2E+AyWt2XQhdq{h!K9cWXc<#t7ABpDWzYoeW%59P{+$EzGk3>zu#pHYJu4% zVRqMyp!#yPC*vK}yswM%2UIF1&5v+{Gb=e)dQdx#YR6Ib<}@68#nT(vP*E%%MBDh> z#>$+^lP;EGuQ$n5V|Uvo#`-7ItPT_?cXPjwse;p$&?$CHr)75qWFT`|fHRR1tBf`p{_aH$ZZUJP z`kh7W5N`GTc()5uG^|x__@dz(FY-b(T#ftO6I$l0W-S{HA0A^~^Uca#&h^UEi98M~ z68VsThZ}dfgdHsQjCk_!Nw7Pue~(VZbimF=dm|q0#c)=PgAY0y)?A2&{{%nuCSa~u z5z5L77sF>`1{1pzxEMYiD>Shifs5fcV**-+VXw!c;U7n)K~{y>OR*ftvQQT!nBV_m z_;{=UvP|oF)&*uW6wCmOPr-74MQ~?4xu0`^eUgIZ0ee3Mn-1&`DOdq8e+o7O*e_GCLSQE@ z#@oIi(S9?5J(q&b0(K-tw+PsGQm|rRkEdX>fqg9nn*+?3g3SeXcM8V*84W3z5m;>s zW&*Y;1)B%##uRKmu&NZS1lTnx*aBeI6zno!3sNvMu;LWV0xUNLTL?^-f?W zOM!(_utmTIQn1Cqf+^S%V82Vjtiaw#!ODQWl!BE5JD!4F0qiF!*ivBMPr}p_NO~ERF9Z12J0c%RZt^wvr!L9}Nl@x3_unj5Lb--4qU@L%$DcJSEu1di~ zV5KRT4VWnfvjZzk!771ekHy!kXe>6`J-p>nQRb)sUi z9sQO^e+t~F;6EC#`GmwX&WXL~50vo4PLzFJ`sdhF&g9PVV)&=!M-ji;F&t+2H4Edvp!%`c&C( zB}>BY`A9is9zq#**y}u$z>>FtEWO3SKZD&6NCir2u$x9zThH)~R=!PjsAu?f z;@bE_mFI)IxdAbUJ7gR1?Y_$ay+gJ+xaTp;0UjY9p}s)U2ELDp?ObaR%&vwTcRH-D zoenby+-Y%HU3tLoL@XbC2lx&J-2oXT_C>s!3yQ_mMu(P3Cq>>uhr*}Gyv&c&K+Eyb zu&f<5_}Gm}b?9`H+v#>(4F6U+rE*3()FM7=*Wz6K!`$1Pn~~BJKffN*n6;;>LuZ;c zI&T5msi4&Zn+mH39EA>fmhM&D0nj0F4u+Y$f<)QwWn;zaT8lESMajoGYOvN>=32XY zA?mjVsbwy<+w$YfsUFq*GbEHa<9$+s5p;6+#`3DQCx>5Gp;f&!U^j>Z4|vV4La7q> z;P7EdcJNP>))bVxT4B6*SfA&fNwzZy*$KPsOJ63-MnE#Pt3THA_{S8S4`Oq(V?F$f zn4d@390RuQ&lG$R+zajl-vfRayd88sks$|jYZ)0-xi}eL3qFrjs_8^R0UQ~ZAv^<_ zm4Fu#S(lJ1Z6@TeKpazx13low{riJBJ6k~=$g_w@Tmn1AL^ccfY!YpkfzKhIZ7z}B zh5UJqinIm#V#f3J>4l8z3)81fju)n9=CHUSfg1`l)8pwGihR1EFk@A6b2)41GOtbCWoU2;B5<;n4r57fre*cTcq$&SqqW-7bvZUtc$Xr3Jyv(TBu+G*abgyyrcuKw_&*s~~i01YkKqVUyhvEzYk(Ti{~ z!p9IU_4SAiplg+Lf)}T%Yn-}PQLBC{qqX|{sFxQrd{=!2xdPG$)vCA3o#m@@Txr2F z=b9m*Cd0M%yg4_+Ree6Tdl7KnW0HNm9k)}wYoUYp{4+F4R$FeE1&zyAL*u4ArjeKD zDuFaR<5WdIIDqtDp7dV{I(ig2O>HtszExCAxOdGzS4`c8N1W1yk_&p z=50;Qn|JNqzU+$ftF4=>8vk)uL!-N{dGk$GH*6{2Qigw9);ia2s@YIhcC~fU%A0T4 zT-)ewg21-B@7!6xb8pigmaw^Q+s?))p7B~TUq!0 zb@|n8ySsktw&u;_#kgv7T~qzttlZn18@6n1+_|%H*A{ovP9>ASO&$%6Tkl+qtCYW0 z9sX0;|J(I(gZD0X)1Lo8|6-7M8+UEnwWoPAv!o0hcb(h2XIs;j3FGFijk~tj|HGmc z4ULW7s`|U@>$Wus%dZp6ch&FOb4B@vL_y zi_essCuzg@O;cff?%+t46fcb#x&<(lfs8@8-#Xt3Q?SKnw64jd3Zn|3p*->Bqk zJw_$DL3|x@`Dtxc_v)&eIq7rN7JEc}`^c$0>0w~gecvj6UF+zc?TvqV+8?muUW(QK z%icYHwj4L@YcBtSA|mMurG#HTAFE)zPWYT2xIMMQ?!bZgrkhsQY!Ft~)Ku39CZVKR zFkK^TuW#5Um`a*W!kYSBEL3L_GKiG1Qh}jjUTP6m2uW*u_Dc2J8*g!A!0mK5-T8%b zO{sFe&S>~dG!|Ym77foBiH2W_s^JZ}YUE=0=8>M~E{0c*Tr-mZhyZP*t#09nA3PX6ID3;w#># z?2&+>s}xPS26W^3*7a`=5y!rN_tJ#V3g5eW-@~trSnqy#F&1O80(=W#bm!?O2VoBeQz`np>zX4?56m$3*j%4n5v`>pY07FLmqVTXW8GdyR6$;P( zVzh4o@x*2Z8^06t(Of1hUoO;R3|Kc}W+hv@)_qr94cdB(-P?@pH4aWv9rH-O;q=yc z&TTPxePjRg<4>bT^RC8SmF}&cZ8z<~${>^|^Up%to7`K+=RWJtE3VwgDy87bna=RC z^3USRS`kl}Vn@N_hPF1|wQCQ{z`A#P;!m-|hBL=9`aw2(Dt}+Q`Ib$$-gf)uJHE2T zVg=e>K71N$@`>xT-R)KO?~}Pwd33y0cir`YMS^}aCiV_wTt<|8mMXMEC z>($m;w7petH(Y3~ieR->tF=M2b*U0>tspKvKqQeZ|94Ij#I*hHzxV&Y&+|RsCr{t= z&iv+`cV^yM&dfU}77-jSkMHI#@bL5!cJmgAeSH1=y9e|L>=_gs(knEKlt{ySM@06K z$)oy4_v=4E5feLb(BL7@#0?!be8k96qba2-K4A=7eNJv({-VW83YIQ=Y5B`5RvHRdtzPrWtFINU zUHAI>4R35L-t^{Mo8R8@x2@aWd3XDJJKitZx$A=u|NhbLJ$wK0@h6{t_Ic^P{Ra+~ zeeva?uMQtMT7Ime^7z*$PJZ+4snch^JA3Z?^FJ7?E>vH<^y5#LuUx(M^Yt4wrknr# z<<_saYwPaR-@SML!Ea1MW0Uz|v&H(TrM0cS!-k&zJ+;99Vhhmmv-Yz5L^7 z(Qj`3uJ`VDW5u@pJC5Z4?6L8}?&sRBgpQ70p(7!K0^*-P1hb_D%75wPsiFi!Ei7L;KwAcVpYQ`IC3ubX$)6N6&vn8ujzF z-_Xg$CZ+l-h0oGM+It^vDkzB>|4GE0w=8=G^`jCdR4;CQ_1s7zD8T3Sil2kxGor5c zX7%|-^*<>u%id$9*SJ(w*zMJ_*cTFr*S}piVC<|hySL4cm_EJ=J)@m*w&w5` zHQiM+*3Dox{Cw7A8rH|;J7Y%vwJSo;yraKVeN()2*tqJcYd*U3;N{|^1JQ%S)(!pO z{;2Yg4$agT&O0CQoOb;2@Y%-PZ69{*IzoPb>iNVMBm*>QjUT)1KeJF!_WZLo)-}I{ z+*+6NR^j>gCN)KkO6lRAb*l8n`$HsmRwXyj?Q!jLl*nfBWj4!`V*)=);LE%U5y}b2CdMYgo9$hrw_l+!T*420UK0A_Sd9D4{O^ek{ z&H3g0{4c8R^xE?GunQ+jj|?+z-?y{UI;Di6<{x`?)a|dkFJ$IzdiD)^_uK2vTgGmU zeQ{XoJpT(TV)Bw>{w7?U|M`W*oWr-a3|PH%+E3!ss+m!VpKA)bojrG6%=7k4-`%3g z7q%_CH|@ve=hj!AY-=45uv$NEbGKgK9!*L(Jo$Xfu7bgrLga-Dh8_R%fPeW9H>wJf zZ@ste+U>gs1}*tGGK)M^x-)rf-?-g|t%mZ4jbColEUkWd$id-KDIKx8&s!fQ_7@iQ z3NJ71+00q~TDtF8cdyeQcx(yHYMgdM&x!Clz230C&}*=JV`AFGw`k3Q^l2+1hTQ6& zp`7OV!6|n%x-o3RrDNxI9^|i%p0J}vv3V}F?^ovMUB7Hq|MuqIRg1GPir-GI{p>jp zng3kzn`4rGxOjE)KF`^Tx{A*}SnE3^g?0>rFsr{nhPrlo+u?IQx z-8Tx#j+(gY{DMvI4)11;{PFFkanonbEt`IQT5A*Owk^5sw`f=664f_xt4ag^ zIhk`5Exuj8J2^Grw$yZZaF3pb7k~Y9LgvEB9`~BR+yLH}$m`ts9TTNbR7zjHTg<_~Y2 zyB2e3Rr&B;$@BSVuHU&IwQ+fVed66yI?8_y*mq3#y4&S^mTPKEJwcEK}dxvG~@xsV^ID z&Aak}^zD19?Ox|BA5S>hl67j&#q-Ax7L=X6YzkR_=*UsG-uIe**<%{jcYR*c4zE5p zdTw4DZ`zUC@T%vtTEDGj8!M;XJO7s7*{03%0q=j^u~_YU?9;ZmMZwCeYg%Y}~* zoQJPJ5HGOCj4fj3M{YdXZF2LqyK8?gd?7UG^VxF;?bvl9^uX8jGfQU7irbRXbB{RH zZNd^c8YK`Oniw3{(RgC>)Ki~rIa&6~_gB|9Snow|uPvxOxMRoue4)Pm;cGPs4H{}s z+=@4T8ug~nv|T-JEOtNC?WFI$L3KYJ_-qi>C+FeODdHKqE5@B#HSqf$zg?fUX~Y9@ z_b>*H`S1{9>9cQqrg_BP8}*xJR+fKLxj#;$tzB3-Vr=BTgL&@`-L`x3JKtGnH9yyP zY0Jg#{Q@PErX)`o@YYW`3le5XZ~VHcde;jRJwJ<{K7aijPFCvLlkKTLRXiJ9+lbu|v~W-0&Gn?@wLwsNmfFH|KYF-kTNvf@U;fvC~SD-b^60&CqLVJ_SBu}wWkWRO??^;9N92Ho;%&sx3%H$z7KyTs|3r(Wbz_@ z6jx*)*&zDz;f2~7^>??6#ghg}t{h!**lfJI>Qdqt?^hvGIUx-xoLd@F3}H-<{}dDF zK90DCx*>7pP6B<4cc2{C#HaD67Ifu|ODYJsN~cxr*C7Ifu|OD zYJvYQ7FdnX|0m**B6}oK><0Q6s5<@$9{^j|l{1jySBI}al#Ud^Kv!lX#Z90)Kn*~T zfQSsF@Bs1w>IozPk^{v64FysHX@HV}rsMG3$CL)~OrRW~r9g#1Yk@WaZ3Ef~v=?X} z&{sgmfzAL`0bK#Q33Laj0q7ABF$d}bQaaQR5%&RnGTc& zlnImrv=pciXf4pcTkfWRBkwlIv-97`_h)t73%d3>^nC^Vco*GgAY27>1?VQw9iRrF zKhyO;34a9bBw%cL0QvkEW2`5nNr2=)Fk9`UMj7zCV*6e8)DvIrE07rrpj~%Uaw0s z9WGU}v`E#X3c}_}g{0sNmoQhx6sf!cp?KVj@H87JnHD%2069no$Moej);2z3a@zar zA{jI_z+9QcGe(*#X__(6{k2-1w#nU%EYLO?hhT&@qpAJ16-`MSE1I%5)>xTIBXM>~8XrjH&DNoc{4R!9(USk!`Rp_+LN`*oz*VR~&8=>MuIe~5j zrDP0BxfVqSKsiX}XVmJDOl%~f-4q290Rm_nDoWF7ij?(8sWc;HjdeSp(AHQOZf=b=3sR0k%1$=rPyO%zqHYi>VM78_l6e>88sxvN1Tn9%c}7dZb#;hnK@69wLnkPMl+-2FejcgGzRH#8&8DA+nNY5TaR3VwdNQK*_xejSm{5bb`$W3Z5T2W;kDx{+8 z2|}Wk=-immZm&|_@sZj%9Y?a4Qd4n&)}d9Dq@Kf(YSC7St{!n)IE_A3k;-B$uWKr_ zmbrmlP@fSrN9JYZ$~=ro<`cN}eqdv0y_9VUr$5KY?eA&ygZ4)Ac)HONT&|FU(U$KE z*!CwdQ$DuFSkFd<^dIFRrm+>)>b`vPKd5Cgq1cWSH(R(3NV zYcrJbWy3Dl)Io3hbPtaijO3t;gLE7tl&&C5Bs^h0`3kf|IYE}!@u*QIEOXf*V_;nGGt$JSu;TB$*Adh+&>Yz&5o{AfSi20rus=h#(|3O9QNwCB8BmF;Q(u zLek}RgQ(lJ>}5)4Sx`EZb>c}qfE+Hjf*x*`eS2)~TM>*fRS_yu)gzS( z@`^z(Vm@ehdq=^BcC-(pNj9{t9g(PbWq2%2!1|1sZS5pz5*T{|n#5>lN71-Wh}qCi z0Jm8-L2b=G8L%Q$w_>Ec&P*+=MKYL^d_u>ql9&xjLaR?yIyg=VaipMRdy1VK$`EO@ z{AMHISp$nW2lz$@ryYC+%W*GHX3O}Q_h)6Cbfi3420|rMk-~o#-j|-E@_`q>0AbZ> z3Q?!2V)y3 z4$gGg<2*L?E1PUNf}W^@RTsfU8Bx4Phl=MH%+tvWaIQYKn`~}dhkf+h%kZ;d{co@u z%j=d?7jNUuvk~^i6x3CV>kD?g2lML>ER6IxoirdUbJ{M$pd8lL4vvtmW~B|M?{TCX z((LJW4t8>sQRFDY)sE=9cZS19a+E+|packrG*H`NFMkkdBIH#7kwErdy~?)dOZIt^ zwafd^Hr&%Yz*?A3aqtZ+$31Mb%sz$?=tcfo=%Wt{!cg4ykaM7440^>-wpc&{C#gUZ zIE;IR1irdsMzoRU60HPrs^C4Z=bDIU0*AC5Hod$~Od%%nXk1yp`@GkAG!NEaKFkiP zaZL)Wfv^s4g&CUu$iDmF$%rZriC{%Q62yqwDw|Yu5VH~q9*8|j$m>j1Kq_iOfF624 zy~~3V?YT(6&8-gH>(B#ru;GD+SIh(C_5S@=`;rehUId9+_z#H(;^z-NP(mu%cAV7U_j51u--LRD?#W8#zr7rWz%t3-R?<d0lg+J{tiYINV@(Mo8f6%SqJ9WRzq9lX0C~&PDA=s5p zbjr-P%eds!I#8sr=R{Go5y`nmj=Z0-N3}lDXr0&{_pOrQ5S0)%TD9FJW>Yfk!gJsq zm{tjKjA;nP#g#o?*Z>7$p)22HF0^3%B}hvsfWfxZL?hOu&;!2nk&V! z^IIuYUKdOKSc?#)5+MfsIc<22I_M?~h80T8xTAguHoeFQpM=W@B;TbbbUsFj*#P@M zLg(R`Qcp-Z+V|94AVOEj;)oOMtf z!i;iYR|~oD&eUFtRJ#~Tfj2dj!lF=05#)5ry;HkjTu(M+(8A`OjK!6Gs=X z<0J?asr4t8Csd3}m=T&{<(-WO@(6oWHPOloM)G?wU&G3XD8fjPK_>ZlLMqKiZxNf2 z_cy`dWtt#y~H>!gpfDNWcTjomN0b**vO$WBmBnQr89=}UF0cm)Ti}i5I z-?JjbDJKy!B#l6dhr^M=)gF9!_g5yy>8Ox6?ShapmE{^(qfmd4yB$fp)5uE9@X#T- zs7fIr^aR<%lor7;MW9xrG9ok3b7(g@hLuSPL2`j{Uu#n{w>7}WQ-1$~BBmS7)QV|6 ztR?;KSd-o;HP9j&H1>`Jdl1DNtOCR>=9wxJiLF!!QM@uFt^mp0jS;ekU<1M&Gh5^V&vcO+ zJoVu3IMT34L@}=zaBdF7GDY51Q>;8MUa^`+#h&3Dnzxs~$7Y#pbEElt6bU>UYr>KR zBEKs91YZVONaH+QHjg+QNx5vE@m=y{iv*A-9egXeT%3QAXfWj0i*Qc2Dpbsap6BjC zOl}M8fs3(hH;~G9)YOa3AvAiM1SGbtC+y@fcQc7u&M|qi{Cg`Ze$OgUIP2iqSqDfl z5Fe*NkCLzlyW4@HpD~iqCxeXsR>H&CPY-L4G|UFu<61efaMO{7JW(*l-ughB|3APYp1~ow2y*{kk;EBwMUNJ+_(RsVYGo+VcIsgd#k(mTawEy}l!9i;6$e4OjoTXHv#V9eN^>uK(m)8Yyy7T__M)}01Joz( zZ!oN!Q_Ym^Z&}6G=2U~oZDg>t#jVM`!N5H_&-Ji#FMFj_NTUcHjKRGv;7=_k zmu;}}kQScqsAQSb&$z+rgG^mwaC&{|ahfO6c8Pf*0%ngl+F%tSsVY=OA>u7c(t(y z)1OBDvGm7Mxh4vvzh$Knl$m7io7-C>2kfXFgSt_TpGw0t5rFMyF)6$WgVX~H?5$eX zQeLO0Qoz^WRr6t~pT-YzZ0poaH6D?AYkUFQ*r_?mJRsLY;|c_<6Q1wiQsUUwH5Q$l^L#Bg{E4M$9G!|U$NRjb`82HX%8 z;$iO9Fpuiu#NuHk@O*SZadeVrp)ec~oH;z=sD5eZw$szTjoT7%nl`)0u)}O&8)Se0!yY_O5W&$a-0y*inlb2yUS} z&nUJM!K>U0_q9}Ubh{dem`(9Mj3I_s0lS6)&9xqfVKr$$cdOtQrZNcU!z4afV_n|c zi{>%}7f#EB|8yQh@IFxa8y{nS$Oz(p-i2>V)J^%Nrfut-`j_);5OfmBe@oacB)4 z*0R3sD7;<3`$ujEsr7BI0%n&jYi(c^f8?3bb2eCx@Uw0(v|xq zvhS+9(WEW|`w_sSPN8BI2fXo;=Qf!XFsJ5%|5WTJm}#?2wc+>_TTCVe`A`^mmoC6< zObqx}IRus3Qe@rJKnRlR1v&v^ou;lQ1fTBzrb);jQ!fyFYZV(5nIwZID$@34f;}Jy zxs-@Ey#%itvq?+Dm%L4cwfeWI?aGbPJ0g1rD~j*x*Y zO^d@ji6SO*W?K}@5!JHrj@i0n!1r#2)Lb#*b>?@@F8Lr6PSU68& z*2hSB95WT`6JMCF%p{nCWJ$a-&1Sh_P}YLRSIl^VvPD!CK7kEZAJ@gH2R(r))I{bW^?)a^zG`KrLLK!47O5`Gj8;cWg4I4xU_$lrc|Fv^E*RLPGR$Nsb5j%Z zebxLfDJLpz$GOn{K~NDL5UO;iz#|SmK_CNAB@ps}IFJFT5(s%h{A-ARO{F)9r0Gpz zDpZCl{LmJ9Gc6t}t0<`xPHefHTCZF#s}NSIE8u7)&74Y8ai^@A{VK(%v z>~g1H#Ks=q>GNqJo{`PjoEodXCy+;n<&^!-9`^5q5xK$b7@1b!!6D41v|R-$p&Z5r zs{)>LBYNH^)Sd4byAKcc4zLnN_jGAsrY?_J*xPI!{kUYus0wQ@WVM%&i!E?@j)-zqp$n5Lfu*chv9Nk>I*rmv=Wm$ zV9G9-O54N4>&w%5UgL(pPW7(ma-A=Fy1 zjUi1E?-PGqi&IpYQU;}{(p0*%CZSXX?^PskyQEJHDT9-fLh{-X}d zFW`rs2AI|s40Yv@?Uu_nu@v74v|Flefl_=U&~7*2k z1T4x0>joIjBueE?6L7N89bT4{+G8Ct&+supq$pxa186Y=;XL0@!93tQTPGT(D5U3@%t0V2fQa z?9a$@!6bmCyI@klrnz9@fIaJi^#&}#1&aV|mw-lA zCUC*}0@l&?WS>U^W_H2)0aovV^#{!4f(-!dM;A;1*f|$02C%PPuvowjxnKhU`^*I! z1lWfz*kHikalwWFR_ua31K6uBSR7!>U9h2m<+@d+KxL{)eJMDsv1MHX!HXg8pF4zRX{^5c>3s_03ay)+LVV{Pejq3<; z+mO_aeOq?4BB_84J%W%Y8-iPWhg?`&EZi*EcNj4cvfuXc*ms^CjqXf$-t)>Ga)h=| zXDG5W)Vnhj-Wif~hVWZ+VQZ*Hp!EYE2L5j4;7p`A1bbxJWVjawE{3~aeCLZ=VZUtn zjzVX?qcW#|@|+mB{xrlPtrCa?It}t_Ab%i4c~fE_f1p61X+Sf9v_Kg^I-o2dDAce@ z*A4CzS80z!%f-ZT;P_N`T+5emGSiyINjYiCOo_SDM+saB=LcmP?7rZy!)9x(XiD8k zF$5}uHk3iTARX@coIVCU?C4WBeh#>V#Xsq6`D2N_n#X(5kJ#|zohbgg^f%j1jdSPN zZuuPiVz_VX9Xm(xJi)i6?`YmpcUTtLy$A4JX;0ErYbdi>T5VjKkizLPMhs^ZG_k{? zInwWrO$mO!;*I0su8-?|$ypP8&xhDk=2~dWW3-a{4D5AYabV8dK%8Ev=In*t5D*v0 znFHT6a*HdQo>g#)xmzon&LE)I=0~t2J`J?`p};-obtDd(mlUM0z{);xB!% zEFJ;Q)bcu8;pY3u)EfxT&C+D>FT(uXgXb7v6MsXdHNf@24Z!n(ZwFoiG#dWZEgzU$ zLy?&43n$~@z{3%j+Z`b?02~>8Alw5AivS*okYWJhy7dJ4fe?qOMF6cf!2SCgI6EtV zN}Uo^cj*A2R{O$5VY`Nkl|HAOllwXB^=I5)bxP zzCpNtPsg2eK0L{tTF-kkEz@#f^t@#f^t@$_`u zJzekc{QuuH?!*VjjD38o_V3@cU4>ga&US(k5xof>Bj!oKzlfrc(U0{S!l!oNZzZ;| zUQN{RPDc_xC*QK+lkrVk#Q4YwkbF^bvPqNRJ9h(6F;2r@XmDv2 zq{hL%`80U)3QB=W9BCzR4@kR9ra{_a8;8|}!lUS(>@K^4MX&}Gt^y2eD05;ZC_KoK z^J80ion<5JS=c*(tU_;!eASyOUF}WjAshnXgAk53R8m<$!`X9!9!^!mG=d_^op;Hz zD5<$s&!IdGLw<)01nIRr?xk2w>{uVIn<+*!zL8Av)J|xY`Fd)Tnr%7#0O#nWjDe$q z+bNDVO3l%|2{|Ih%Etvkjy_`{$0R?T!_Q9}0n+%4XCRae33ZTy{xj~}OV0Bg{_boo zxBEr>?6Omo1VKLM&Jdq-P?vL#zs6H1gTY|!Q7z(W1msXOvqlcFe^JP zBX8!UgmI~{sWI?h>IBV%X(^LqVxCd-8#8s>%=GNETo9PEc>aRS1qHeJIALbSoCVpb zX?lI$Ox>KUtnAb!*|}L6GiNVÊ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 G!T$tC;et&7 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt117x/bdi.bin b/tests/nxpimage/data/bootable_image/rt117x/bdi.bin deleted file mode 100644 index 0fed7fedeec0fd6112ce1e88f2620af8d9471196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12 PcmZQzU@!D%@J@gvBp^vf zmqDlG;GXEO0K62Mql;7h(B4+y?%sm5_zl z8!QIOft5fNuoh?lwg6j!oxsz;Uf?<4W#Az2D$ov`0^R}M2R;Tmfv;#?$_5#lVF9QdGSAllm6z~r4KJYQn349HN zfC!+gL;k>6U=lDDm!0u8_xU@Nc_cpBIXJO{iC90Xnk+JRHRJHY$E$3Q3WH4p+KfDZFg+ND5? zNlD%KXQV%hdvc8Xju`js7gnoVia~dzzw;-#gd|{nTmtlWUjLu>=#z)YLD3;^BqmSw-fKFG)*SDr z9B0<}bC!_c>aY?jbZAT@Aeu;KxMDneL7gbOY;|IAt7zltRY?nz{HRy= z)$XL%zFO!yAc zUnykCZcUGPMUm@V_xM-wp5CucdB!50r`l7O?wB_BxWjw=;1#8hofjW2AdeE_J^C2x z+Tv`LG^p3Vgvqb_I@h<*@Et5X-|3Nvxz}%FzbR!IV4;JUh6UzHty1C=F__Sgmf9vi zLBnm55GW5ua?>4pmK$l`g)!)*IQkvvd)?`ALb^H4;cF%)`CA^zcUYEuQ_w6w?(+y{u|wB2O0?CC4fs47_=+xPE|j4k@g98vuWU!Iq9fladtbd4Zz1NS^ET<+k(3hZ z8!5bdB)NEdn3$3RG&G?^-X#W8MXgPVF+5D#Q=k{~&jYO&NPx~~b=-I)!S;(T?^fZo zU`h=nl~Vt5XKE%!frZY!xH6KpibWf`2rln&7Xmp$2_o@UWZ)`Lxm>%c5z2 zB5kb~B3Osa#NXdp-rd$13B3~;R+iCn{s=KA_(_(SnY2U*?zxtLQX3jCbqWZHN63@c z#4-UPf9|B~>*=M{dhZihCGEenEA2osi4pU^rvU&V}6gz<2D5&BDl59omS*={h-w2rls3kYLng!Tdd!Q9Ld{Lx}BAp zMev+r$H4r~Y^J6^T%QS9{YJ3EKyVd5hx%YoU%R1ZNf{*>FZVj8eVNtG%I&nSWpcGNm*zL2ttP6|=R79K(!Rp0{_0O2iiXP0 zRRfv_)$3laxndxGP`&Qr@ue)jB$}fms(T>E0)#cPx=vwro$p_O5HGLmJm$9Yx_Yg1 znE(2^T3C)=>kL-c-PSvp-0eJPuXS2f=RnRT7M2;+{pSl4qf)XngSlBwgX%t-=ae=O zKdA1*csw23UW{#}wY^qdRQEs$GzdGu>i+Ttg0W5edOjPMhj{%DK;n?~A9xuLS%1j< z*Vq5=S+0ky-(&SZVSR?l?dG{3vVMp8>G&DQ{RtMfGpc*_g&k2T*}09mudly{=hQk7 zKdAoAJf4oqL)OM<2{uG^50pScn4i^u>IG$-uSSS5Um52v=C0!9Gv?EgY0SUA?kic2 z#{3n^{LEj@ zF#{%stV5_+$U|^cAA6uh%%8>){gUXx25dVO^6rVv2G}vbx+YJ^XrW)+6R=NqXs8cTn)Ad^3~MF_Wk4(m zioIHBP$b{LxA_(M%9T@LZ_fJwF>5;FP^X{wP+z)3ch$7zxMTP6U!hJ1Vr*28R4Mt* z;s=!1nl&FRL7F{1OVCmz2lHt;{fZPb-?<_xlV+#i``bGfM#EqMh-r4Q`$R4=PwdF& zDZNF+tm~j7oaD?d#dnR4xg0vep*Kil@AJ=KzUieV_-X9YKqPckrsIR;n1W=v?35+w zg>154mP~NbkRnLS&Pk;*zN#drN19lcW|v_*RKou9NVGJxEQTUkG8Cnkr9pNSm!<+nY zq1gNt&}OGCCld1=e!7NuvuN4&6;A0WgKsx{`{A3|LED%~B41qxZDV^*5n75Ye-kdd z63bfZVfJma=h%2l*>g6arJV9y$Ta56fxScRpvIhBmPU5=NI7T|+G;(Lg|&{_YGzg& zdro#)L$I;%WcWbVZLke1(URVr$#Ct5Yj@7%GTCY5l3A>seku1znXD}hJbZjf58Cf% zGVK>qqqea<2QAA>PL}_oi&TX=Xp7RmOyMcjmQCP(Itv?fw58bbv0B8mG;L#}mqb3^ z8c9HlQyY#Xc(EU%c3i9#hn)c32f_9-VGMg(lkuC2kv!uX-5+YO^Ayc-{=~q{GLLI! zna!mO%z$eKT$;cP$QiDA+y=?ldY;%b1?&Xbh~gn$7GfxZgj1DT$imJS8SHAPrxkl0 zfoh=X6=@sOpkcDB?C?d?D>~Kmq+Qjki>#{HpCf%gXr3#&#~|sq|c$N3pB79w9J1y^JViHq|aH58PtA_emgBWCO(5KIaEKiYm98H zPhcZdpEH5Yf!RGh(s&j=mWPjJ;nRB5IWU5UllD}ew>k%Uq{7PyB3+A=QLoQYXMt8t z6SKb1wM8Et@5X4GDCY^9j@%{vp*JGs-QzLu<}nLg^MOs;k1;<3F|S0Fu=vv+NyNU( zBl)vdIUNC6s!W)Nah1fwZIX%Y?an25q*cy2h~FQLr#m>Cl)>WnvH05%e?A&d_j5MB zH(bTy#~?lyb1pzLF#}4T?luKneFq63P=h7Jh<$KLPyH&wlnZy01+t%fR`M z9(|nDp(`7eWvFI*SA=*-=?){`_p@)#oO*$+gLKZ|3~Dtyhoavex*pQm zFSky_j`p}$ssGXvoWBAafM%eTV=I{A364GB=YW3#UIk8aEGi}BPjH_FJ_WjfJ`Nq? zMg!R#xnPO~9Q2!Z!XgH>=t+&g5bMXLKmmR?vfuiE9qx7PR|+hEyEZ_Kkx+Uh>~sb+ zaHYXj86cU|{dav~=Zb*9+>gUu9?&rNR=7(7B6GLGT@(-@{Y`(^c_&g*cRfO_NJ-ri z+&M@|-PLg4ft1w!Al$bF1h~#3WhQgIhqJlL1leg|zCXb?MmY;Xm>#_s37v#&GI}o( z`ZZkf0Zs6Qo-o$PVd%N8J{hZ^ri1qAmvBc{KKf0Y+)YR#4i?sbs3EjJcf3FLPjA2d z_9N)aXMy0+$I+iHPIbOdgpRlRJkU`dB!W#cP!=_^&`V_Tn>ED;WR1|AKF%?^l!XWV zeHury-Zaxgj+= z=klTD#1CXC1t(5%(5MI7+(Gzp~~s0Xe0ITsxRquY>y9w9xY-`2GN2CG+ir?(l5_pe8=K)|BkI;=M#K8d{5C^ z2izL$fQ04PQ&gsnT5MN57$X@{n|oiO7IqzVoi>O8AI6>sE}LtfTSlDj1DhLPTco8W z@I56nJwh%m1r0TtUuUTs6&oNi%~6R`^&q8q)=qC9s(S-;W>!RAb4jK23rTmfn&3U* zu%rn-h}o3k_EqnNZqckeOIe3;>#M_VBG*^#t1|_|%awS+mJu()cdRb0$X9)=ZUy!{ zzH0fYlUGtSzB)EfFcUIxK1(VvgW%rhUdm)pJFHVa>AWXHBX$RB3`IU^zh^(8->kGE zMITzeRWJ|hpt8iQ^&@BOczo3<0ag~cbZ8qICnAoXJtJ@FSlvD)PwahATIqaiCHAGH zLM;#N&kXG`X=EE4&q%Q!X?JN*HpSg-FQo3tWwfqEL4umm{On+?EOkil$q-;A3CP=1 z371h!E~2_tA-DZ)1p6IXszk~J=%iuiW5NU;wjZS)$4X60nT|PS+)79>unF)3^t&|a z@h|Ur595{uB9YI+kqB;m?X>-A8t8WWSR1cGefOisP@YG4TXbV50)J)H?}eYY4Q|ye z+^!k8522%f0!Da6&nVpKv|t=quX&{M?h85CJ4vugH`3o#>~|mgJ;#0xO)Q-K?qk2_ zl#phYiv8|mzvq;YO-z1M^oF4Q4)5S^hL6p}Q7B7Fj}JF(9?9k!!M;0{ZuBF*ao7AF zcLr8>x*B=h0$W7|tfDqpZ(qPdMe9S-%OnTRm6ox5$U%A+6IWUxjc~**d3Le#T_o+^ z(l$)`MGLPwgqe6+fjmM0L#t@tY(KNPzszu;hGPtbVqr_mvTWWt96^{r+HiQieSId za$2TL<$4Y=J&5ThIZBL?$Gi}Y@v`z-rWkn4-}4v~&)YI3lhvB)SK7`pC6&v1RoQM; zi&}%`XpEZk!2FG^1Lii?ktEFB8DWvFBSLUg#3RvNs|S1HRrpQK+SjP2-o)ml(zp3e z8%sSKsZ+zW4VIyQ8w+VomrY^*DK^-I89LY|17TFAI>xWa;H%V#Z%Ryf9zC5?PAqM( z9qE^5yv7DA5i59iu$lIqhV4S}?Gk&?muf#Mc{}q=-2pbDoUANP^n#b=8}$dhSouzd zom1jVR@40qtWNdJ<$I_nUBi{OqV=V>HM=ZZac0tpGxlAUN6Q{9-RFF?EMwRHBg(!_ z=Wk=amQwVcxc^9+cgvvIX69>(#x`PI!}^F58PUI?tiE)WQx{lQcGqQN#VV(}Z0R@C zMy_(MEi3;f(zMT63t3a}fLK#XM{Jt2CMK=3tcs=m8=m$-o^};adp}QmA5Xi2re%AN zIDZA)_n<@qbS^1l>virgu9AgW{zcqJ%o!cAdy$2>vqt}B#fHs;Y(83snB#vOI0= zn6lBO8Sc|Hqss7xAjjZJM5+X|kX{^Ahjt@-UO6^T$mQ7pI7xMHgbLKXK4#{23mDVO?n-ceI)cLY;pC@=tJlwkKv^lK=V=eB>(sBj)dSp z^r|;Pf9dY1KlEw;U%NZ%3q9R8`0nUK)N2XwUr{f`24wGSHY%gZfOfyTkFJs#abkBP zTO~8JaG4B67$q&L#f}kzee3xnG?ebu|Pa;?=+^hZIw?)*W0Pjm0% z=(oRmFDC?pvf8$P`xoxz`a^SL+IH&=@8$YJ7kUS`?Pn+hYg;zQbX`Uf)~?gAaZF|t z-q74H>$=9X8T0L4>KfrW-r~aS1HlD(61uIj6xTm~2gBa4#a_ zrBr|Do<3!_C}5{J*+TOe=b$HU1rf!a0Jjb2%59B8&~c6KCry^2vgU}QBN(HDrkeyE zbWZn&+InmgC>wg?nT9bjlH(wm6wznVbG-gghO9`4F%tA#?OMN*wo&jF!zrOra z5PsnX;n@g3bA#~V2!HDa;i(8eeuHo`!Vljdd>F!CxS9dHFvL8J_gWc}#ytF^VWk>l64JgKBayJ=_-A@M%63eTGsd^k9;<hu%D4{|kjzKAh$d4+W2lWpR%AQ#E&INWz}WowU5P z4%0sm7yVAsI_8%RbL@8H+B06)Ots=oVR*YgJZkq6lprMQ9Aw&+j@P<`)I0pIb!k#( z;FqLgzc~G~^M|zBGbZVix_yP#_KVfeyMJ0Z{6p&B1Am$$%YA>k*YJ}Y%NKB5PG5)3M-+V6Q-qtPNc3aRM|8LIUbQ2vIpX|tVzwo6Xif4X|JYPeeHH-ea{Ze{u z@xjgwhQqqz_GyL#&Mw_;$g{Y8zu`;6=;9^qHO?u{6P+T_latauU0lcTvdk>n4+dxB zVw_57GQ`9)BM*D)1S|Z~V{U?zo^(T~GYMg_A&rZFk2W0mF5B42sB*LGag7i7a1tft zZZ#*cQ>Af^u+$eS#onB*lQfR*#BsjVYG|asRpWgUy>F%aa@@k;4EbNr58S7EUwM4M zlA+d2lM%K9P357f;L2$6HTl|RnuneBwdI?WK7cLGPt|Z<)RtCkvB}POCCx;dMt}|( zmdtsax}-bk>A89?`9$=rPljIE6>sw4)K1k<0YXfd+ z<<@F?Qq)<*JPPdxLx^F1Eo84U~=k9XjF$p^+!y|RZtZQ|z)g4%z z5U|y)nmDi8(DF!~U{bn4 zRuz4Tc2aI7GFoWwOXhFrX|-`ZF9@jiuPl5 z?;eTjf$X`Cd^RKL`*>ou49Vw?fK6(vQ_hm%a$(81Fx`W<_&W?`g-AA80q-D1#ZBEu6wU|eMaxT6S z=i(l;kY}}LJso2tn7xga+{g?x}%| zl%Uad6^qJi@jv%jYY`&Q5Ryf$e|>*=HT6;V<$fh?Yz{Qs2|GYAPr;7PFyWXp1O27z zN{0?3*^3;=o+Oiy6$y=uyawGyjc-F*v0Qs>1GVzRJ-9L7W5YK_-ax<7`5UNRad-b4 zXt(?DVm%Uiis_DrZe6YkuObq1ea<7%*gY(EMhBI~n8q4I>!g;$gK~_DDTf|bKm<3= zgUf+3>aQUTyRt3yY1-e9D3{2NYsF(%iul`G+BZ|KqfgyWQ3 zIdA5?k@E)5ZqBuwU7R1{`~c?_obTbhlyeE^MVt#bFW{WdIgj%!#{Hoay|jG&AwTB; z=OAa&r^M?y8#!Az+c_`e>}1><5_mp++<%SpRnA?UFLC~y^QW9IaQ=w%S} zId9~=fwP-)EoT?!hd4jLc?IWtI4|W~!g&$r0?rFK=X1{EJd5*m&e@!&aL(jBf%7=d zqdAY@JdAT9=Qz#+=RP)$dPCPZU*+7z`4Z>PIe*Ie0_Tr7pXGdpa|h=?aej;QZ#log z`8em-IUnVGnDfs$|CIAfoS*0XBhEkI{C&>f<@{~VPjG&m^ES?pa&G0kne#@@8#uc; z*K&4oeu(n}oL6wZhx1a-C7c&=F5tX?b3W%h&a*gA=bX)X3g=AD6F86KJeubVhr3Jm*N~!Po>Tejs$;F;R@g3?y`iE$^;BW# zYgHIr{_Jc-n`K>rdI4taL$YqUt*5OUp3NGr@qi$ z{0$xn2qyKlg>t?^?euiVfi8iNY4q>j2HxSTrIqm)U;_SnurIWGNGa8~IWk@d&=)>^ zq4Iw9%>4S)?TAxOP6l$`kGJX?jYrytQtZb~hvFVo3cPk4c*{uNKF0d!UV+wto}0s_ z-KGf?esi#y{=MknJpug-#jmgEx7(aH$>VJLCEgRG)-iSEyIq`)qm8#;(eI)=;g~%t zdrz(G*=$bsSD%kzuiAo_V|a&@l1u+umHxd!0KFkUCO@g3cMt5<+n|^Jr7L#h_@yQ8 z#U6;AnB(4@-iXuCV^XWz>W+j?;Y@TuhkEbE^4(^O(Ln3rbLw$}_Zr?;(Vi zE~3Baa@KPBG5mcfUXZSGs+!*p%hEh(ehn{RW=3M$l(vV`7xa#SzK|(n?WNX7Z7(_^ zu5T@$l#jmZ4`tIE5%!nGy&XeagYV8+OZ#HlTj??7W&`Jg&@j+TJ2$A+z)Q@Nz>DV)2(6 zh{H~q&Jz)DUGSF!?3M4Jxx&{Mb;kCG9vYajY=o;L7~>+CD&S;=zK_w!Qc@9nJ%U}m zN18WeM@}u_r~8h7mSBhFevAY!y-tF0$9{6SJm_6+52wt>U+>@1bU01IyW^^)j%DG5 z`S(a-ZgR05u04`Htet(QZd&Am{=eq=p@e6_IgWiQ# z{Ud^r)Div>-CN63=x^$%c9TQQeb%52=);1Im@jmA4f^An+^d~J?v>7yIAJs4HdTN0 zq*LqIjg~a0%kSc9O|oc3O`LI4O;DpR79}8S%d16DJ8i zrqSH4@zb+I>v4TIZchC_z{%%0$F~a;Ixb4Y_-QptnmfgLzOr8F_Koi*cj6UMkmh z7_+kLBo<45lf&~-i)oPQ$N7CCPI+lbdOGzu8IFXeM+AovXSsSi366dJq||scEhxro z9V7hv<42r%ye<+dj9iqA4%*5Wu>w3DAzAT`Y^3QAUFo%;Rkpd~q+!K^dxPPN&Z;0u zu^b&wvN}eR2LE>p<4?yq5*+wT=U_uv>zIbptP3YOGLfIfK0atKPiV(U>6wy3d!eg~ z2vuJ^m10FmWno2OO&1Xx7Z<)aK1JJ}p-t)1r=$h9mcy+{=}LgRk-CMHE-l=4xKp|` zDOF!=7axR6NU0=mcNvFg^$hPe6=`}>E|Z>=tHz$;L7^w5+gOy@GrVh*!+_FE4!?+d z&Yu}JekqFc+cU-qRwZ4^Wf5t!icCEzS4if|osV$|`u?~aZD?rF_KJL6B1nT zn$~pq%aJn^-%ZpMe4%#{jbNj7qhVK1lk}2;+3nvge8ONa_|I{@(1kYu!PL}uJ1|EV z+rJRR^E%Oy@}B6>&dX}QC#?M={(l;OJkq7rgx=NW(2@xACby3W3x5=W@CRsh2l_d6 zc!v;-2nAmVnLvRSGDw#}(_MR4gI}5oDKdJn7SK`i*M9Ym#>J6H!SdycD(*Eds;DTh zFlHI2G#iu1j5W>ct6cT2O%07zF87R@c{y|Ne+4nFajkEzC6P#k#n-HFs985dnwOJ1 z*GeOsYB$n2O`J${V^YUc9+jGyPB}F_aagQBJu!I%_3L@KK0P^6iBDqkiTd=U!SPD^ z)N}*WGmuUR*QckAQsfORUwyjDYWQFtk(iQ7K|)JPp0+$k8(;ntS$_Un9QBLB1&MB!$EdF{Pse~tvWcdygMjB$qWX-%eb1)*O)il(vaj%_z=lX_* z#$xxw?$xzT#`z131r{%qrIx7}F&&Kt;=+?`Eg=K$tM z4x1lMt{R%bGDmThqoSaQYNm?U&~PXk2rs!?nPbWvLi*g(85QfsUFrYlq~UH#(Xzsrxxl!(cD<{$YIC#NE>W30)?f{r>o?IhSy6KLy}3Db%%F7mcVeWS5c~zL z9qVP2JW#c~WYwZ+%~e#U(A8Yq%-fEqnKdgHi5lJYkk8FoLD)aM4aRLb$^m$R69C@X z61<@$UI1@j3Es65yjdmQH|*n9l{K-&yV2yt=}*skW-H5nZG7dyG8o zziFG@^_#3!o3b6t@2YYvD!6w=#iFXamaQnLxVOqt;wWd$RzIy8EtX9uxVqqMYH_&N zG^~X3rVXy9b=T>p6Y+mnchg_goyQiUA#I_oRnfXB6H1j??b_sue$yK_Y({5T=g`V4 zm7z^aMU^`6cq?tpA@OKJLf?!3N5%x3Gp^OHMl@7aw0t>vbTN`#^{dy{@_xC24DItl zeSHI&fnrX-GiGc}Yj(HQHmt#Tzl)8(0;L3Wd}SMF8hI@5=X+ZlYmK*O)8$lYgB#>S zr_>FURjT7b&HEuV^o{GB`@c}*da@ogXeQo{PO%jO|&3kY~b8S=egY_*g z%bdK~)(49lYBq1Et>4uA;L@TM_cu1xHa9nv6fdfpHETxh3`idt{g{r)2 z7pknNn#a>Co_oInyA>_rD GN&gPyTn)$o literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt117x/flexspi_nand/merged_image.bin b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nand/merged_image.bin new file mode 100644 index 0000000000000000000000000000000000000000..eebb0f775ce34feffbe11a08618a39374bcbabe0 GIT binary patch literal 26732 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 S8G9uW-;XA;nUI%SCjAfSTn)$o literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/config.yaml b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/config.yaml new file mode 100644 index 00000000..a3d176a2 --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/config.yaml @@ -0,0 +1,14 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration template for rt117x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt116x', 'rt117x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest', 'latest'] +memory_type: flexspi_nor # [Required], Memory type, Specify type of memory used by bootable image description., Possible options:['flexspi_nor'] +keyblob: keyblob.bin # [Optional], Key Blob block path, Key blob block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path +keystore: keystore.bin # [Optional], Key Store block path, Key store block path +hab_container: hab_container.bin # [Optional], HAB container, HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/fcb.bin b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/fcb.bin new file mode 100644 index 0000000000000000000000000000000000000000..cf32c5247fb814c111071e2edc955ea4893bd0b4 GIT binary patch literal 512 zcmZ>Bc5`B2VGLsc0!C(L5JeIY0IFtWVMo@(h|D3xe$67m#b(5!22;n%!lD9WW24zv uu!$nX6j&rU5In-H8))j8fP5AO-1-s-*pFK$jbuQf4U7atpd(ns*a!f5nFT-q literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/hab_container.bin b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..2d908b84795aa1d4fb41af87b4307ff05a39ff68 GIT binary patch literal 22096 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 G{r>;0_J4r@ literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt6xx/keystore.bin b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/keystore.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt6xx/keystore.bin rename to tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/keystore.bin diff --git a/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/merged_image.bin b/tests/nxpimage/data/bootable_image/rt117x/flexspi_nor/merged_image.bin new file mode 100644 index 0000000000000000000000000000000000000000..bff854e9245ba6f54bae6a4c09e3d3aeab79da0d GIT binary patch literal 26192 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Ê}HtxQEue^*oi!*1>$(z|(jUh8Lax)j>de6lC%S#>4wo7TRh$<~_ zj+`7oI#S0^oT5yboH}`_ehwKWXLbFd4tw=+=DaSsL3!7N8H!n;LZ;8kB1euS=f{sv ziXWFcF)K^CC?hjlPA*?g{yuFgG(VdyS8)(U_?qDBkiOOK2^C`#QbIk#cyg7MH>2f( zAH5#1?uK_m&bX^9dg<+7p4L?<;9d&1|F7OX{=OcQmgdF&NfjY!AX~$~UXFs4j3ob{ z2WGf-*eo~@Pnt9)WimM?B_%0^l#&s7q;wcLH#2JvDUHaJlH)TM;!uW^^hAgqD{(MX zWP{}7DAK7dzktr1oBdoG47depx%2;2F4tAgP}X6&*kQ8_XzQ@_Xz8$g-N6HI$dHzH z%hZ<2{q2@9EstvB;QXHZ1z*kA5L!>&swP1ZPi;s{|AMPVi30q7AdzCq^YFWWIO~hc z+%-!R0%P39FHZ;<#4~y71igF`ir_2WohoaUxI7w6IUeZQ=Az{DjflYCzw6OspA~%X zs=o=pGQz!kr`=}5vLyHxfT8)-HX9FkK7Keb`05D!Gu^CsuVHigc1ZjM2L25o{-&6K zui+3w9O|fbhdlsBT5CJN!-i!zV|`E{`SG#V+DODfcxK@7`*=ReV#pCA$V?amiYYL& zoYqcHTa=LkwoX;)^PqUXnxMG{b+n<$qsU%zu?@Vww!hzh*ddWE%w8CuHd{AmQa-E< zWCT0^FKEbg4?7>|klJf%eSF!D5Ojb%7 diff --git a/tests/nxpimage/data/bootable_image/rt117x/semc_nand/config.yaml b/tests/nxpimage/data/bootable_image/rt117x/semc_nand/config.yaml new file mode 100644 index 00000000..67a129fc --- /dev/null +++ b/tests/nxpimage/data/bootable_image/rt117x/semc_nand/config.yaml @@ -0,0 +1,11 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== Bootable Image Configuration for rt117x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt117x # [Required], MCU family name; Possible options:['rt116x', 'rt117x'] +revision: latest # [Optional], Chip silicon revision; If needed this could be used to specify silicon revision of device; Possible options:['latest'] +memory_type: semc_nand # [Required], Memory type; Specify type of memory used by bootable image description; Possible options:['flexspi_nor', 'semc_nand'] +hab_container: hab_container.bin # [Required], HAB container path diff --git a/tests/nxpimage/data/bootable_image/rt117x/semc_nand/hab_container.bin b/tests/nxpimage/data/bootable_image/rt117x/semc_nand/hab_container.bin new file mode 100644 index 0000000000000000000000000000000000000000..b91e3f9ddbbf0b564c058f7b1187f90d5d9f510b GIT binary patch literal 28744 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 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt117x/xmcd.bin b/tests/nxpimage/data/bootable_image/rt117x/xmcd.bin deleted file mode 100644 index a4bfa2c2bd2f52b570f79c51324b0b2482637b4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13 Ucmd;O5IC@qk>MBv10w?y024q0NdN!< diff --git a/tests/nxpimage/data/bootable_image/rt118x/ahab_container.bin b/tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/ahab_container.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt118x/ahab_container.bin rename to tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/ahab_container.bin diff --git a/tests/nxpimage/data/bootable_image/rt118x/config.yaml b/tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/config.yaml similarity index 94% rename from tests/nxpimage/data/bootable_image/rt118x/config.yaml rename to tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/config.yaml index 9506f90b..4da62846 100644 --- a/tests/nxpimage/data/bootable_image/rt118x/config.yaml +++ b/tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # =========== Bootable Image Configuration template for rt118x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == diff --git a/tests/nxpimage/data/bootable_image/rt118x/fcb.bin b/tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/fcb.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt118x/fcb.bin rename to tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/fcb.bin diff --git a/tests/nxpimage/data/bootable_image/rt118x/merged_image.bin b/tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/merged_image.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt118x/merged_image.bin rename to tests/nxpimage/data/bootable_image/rt118x/flexspi_nor/merged_image.bin diff --git a/tests/nxpimage/data/bootable_image/rt5xx/application.bin b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/application.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt5xx/application.bin rename to tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/application.bin diff --git a/tests/nxpimage/data/bootable_image/rt6xx/config.yaml b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/config.yaml similarity index 66% rename from tests/nxpimage/data/bootable_image/rt6xx/config.yaml rename to tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/config.yaml index 38f17639..65d4a9d4 100644 --- a/tests/nxpimage/data/bootable_image/rt6xx/config.yaml +++ b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== Bootable Image Configuration template for rt5xx. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == @@ -8,8 +12,8 @@ memory_type: flexspi_nor # [Required], Memory type., Specify type of memory used # ---------------------------------------------------------------------------------------------------- # == Bootable Image blocks definition == # ---------------------------------------------------------------------------------------------------- -keyblob: ./bootable_image/rt5xx/keyblob.bin # [Optional], Key Blob block path, Key blob block path -fcb: ./bootable_image/rt5xx/fcb.bin # [Optional], FCB block path, Flash Configuration block path +keyblob: keyblob.bin # [Optional], Key Blob block path, Key blob block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path image_version: 0x1258 # [Optional], FCB block path, Flash Configuration block path -keystore: ./bootable_image/rt5xx/keystore.bin # [Optional], Key Store block path, Key store block path -application: ./bootable_image/rt5xx/application.bin # [Optional], Application, Application image path +keystore: keystore.bin # [Optional], Key Store block path, Key store block path +application: application.bin # [Optional], Application, Application image path diff --git a/tests/nxpimage/data/bootable_image/rt5xx/fcb.bin b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/fcb.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt5xx/fcb.bin rename to tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/fcb.bin diff --git a/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keyblob.bin b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keyblob.bin new file mode 100644 index 0000000000000000000000000000000000000000..c86626638e0bc8cf47ca49bb1525b40e9737ee64 GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0 zEiNxGF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;0_J4r@ literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keystore.bin b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/keystore.bin new file mode 100644 index 0000000000000000000000000000000000000000..96922bbdc9c8ff668a02ed85e94faec71655a728 GIT binary patch literal 2048 zcmV+b2>Fh&!%lDG=x|%Xl?ESW2k&P3GYc&PYk38)y2pR30bm zE#8?+YgD4)27CY%KWl{|D%{)e0K|6SK}fkU5qY>^?4*%+pejtPC1sQMs5C7=L`~Cn z`-aRt0U8z&i@)3&Lu4x2C;WtQ@wPmoVm`J)F2pIdZ<*lJ=!GFR^T^vv9>3fg7~2x) zda{apy6A@#Weg)nN?l5Q4gjrrCW4S)hso*&zj`bVDPRZ+{QgE43!yktmTTw(^!BwQ zBntE}kT2`D5GcSjzp-cw&lpeQJ;SP>d7>>m9ZO$AALfH7GG0Uhi^hsCN?k>#nQJ9k zOwj;0U|Y-Iy0+gHLR*Sk@QvKd;NUtZ;1XL~blces3Uj>U(Z!H?I!n`5gr@24eUMMH zEHxjBIiu}9y{5QaaDi&h7FS?Q!6RePa+#$5qPrl?$67~TAumZ`@~Z~2$!-X>T29~i zgzp^Wic*t-&Ye^9Dn3#d2W-c%-k&xh>4H)AW*5uKpk`VrY!V;r#3{4QcU4(GOiYz< zc=rW?z9(dfx#d0j^dRVX_R3rjk&>ZO6z2LKG-*=2zYpGzUpGTy?zQ2Uq&b!KZt7*j%@gB+M5Lto>3@s`z?xCGH6zDyN|-WdI$vrO(ta^>Q8AYc zc66`Ob^^NOmM?>OSl1B9LR>0GuSs|f%goLY1P%&fr}{~B4E%cRSk`>ZjKIYF`!5!Q za7&a89mc#*M7mVY`l5I>)f_2-Z92iXvlPA0-qCnb+2Mfrf36>sg@>t$* z0woK`jDU=R3pEGwM`2KtMoF_`Sbiloc#)es4fH0~mGetkU1qge-AiAiwFa|- z@kzHThS4y~O*v?0Cews4LAbHK5hA)2B1^BiC{M`$ftjA3Q;d7FMun#>nCA!|?$~2a zr=gBqo*FYJgWyEke~)oF#48S+RsGJ10&+DYN|6t>bdWi_0tp)BG+8s>TtJTY>auPE$a#ae02-2b>pelu%)B*NLCv&1MUr^=+f(2RNJ5$k)veDnbLsR%QLUejI8o_w$2_~pYAK>3 z?;CUea-fR4@i@Qvy@}XhZ)k#_Io_jfio~=rSA2e8OajcOLsAKubU~rAjVp`A?GD;! zX&Fb3M)qhL-z9&&&ub^;29LE!?4EYy%vODXJ zwaeJx9E`ZTyapuf^W&)CJ}ZoKPo4`W-WimML*SlY@rDXcU=x_NkHA>$&XW_%^OPws zpThojND{uS{5W+RLE41-aWpp4XJz$^?htMGF-nN=YbKAx4OI5q>xtZfULG2+V`em- zqQf4u!bUfDTU|JoQW>ZaUj|KgDF8kQ47gAmIR5g(lvc;Cmc+G%&DxCd`0Om4=XmF| zo}RLF3~>GRhu=i;NkFJLeva2Zk`Y*<7v6RUxDCV->@?`tv#vBiCvo+>RS;WpeoM!K zGtC$8-B^)4?hq9p@Bl@mxbXKUu}o?7JgMI5kzByN#lW&SNnq!0z&uJOv9xoWIhm(a zn#%ZH_E#%;`qULI7$H+tQB5zC*Kut`TO!`&k<+4HPztUrS!;}C0&HK2#Qk5_bVNyp zKA6LLzJPQ_MHEw&jdO~`%jv$jS$^ITIYzQyclmhC?UHN$3hZ4YUP6C^4u0X|mXn)E z>@LrN@%anLq`t(ismsUdS?9-rZ{R2Kv%8HiDRNl~68rJGv83f7(FlA+X#^$K5WjD` ejSsi=(&yhu%I1&|Q#2vjymz|v(At94N4B5F&g@A5 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt5xx/merged_image.bin b/tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/merged_image.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt5xx/merged_image.bin rename to tests/nxpimage/data/bootable_image/rt5xx/flexspi_nor/merged_image.bin diff --git a/tests/nxpimage/data/bootable_image/rt6xx/application.bin b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/application.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt6xx/application.bin rename to tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/application.bin diff --git a/tests/nxpimage/data/bootable_image/rt5xx/config.yaml b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/config.yaml similarity index 66% rename from tests/nxpimage/data/bootable_image/rt5xx/config.yaml rename to tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/config.yaml index 38f17639..65d4a9d4 100644 --- a/tests/nxpimage/data/bootable_image/rt5xx/config.yaml +++ b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/config.yaml @@ -1,4 +1,8 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== Bootable Image Configuration template for rt5xx. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == @@ -8,8 +12,8 @@ memory_type: flexspi_nor # [Required], Memory type., Specify type of memory used # ---------------------------------------------------------------------------------------------------- # == Bootable Image blocks definition == # ---------------------------------------------------------------------------------------------------- -keyblob: ./bootable_image/rt5xx/keyblob.bin # [Optional], Key Blob block path, Key blob block path -fcb: ./bootable_image/rt5xx/fcb.bin # [Optional], FCB block path, Flash Configuration block path +keyblob: keyblob.bin # [Optional], Key Blob block path, Key blob block path +fcb: fcb.bin # [Optional], FCB block path, Flash Configuration block path image_version: 0x1258 # [Optional], FCB block path, Flash Configuration block path -keystore: ./bootable_image/rt5xx/keystore.bin # [Optional], Key Store block path, Key store block path -application: ./bootable_image/rt5xx/application.bin # [Optional], Application, Application image path +keystore: keystore.bin # [Optional], Key Store block path, Key store block path +application: application.bin # [Optional], Application, Application image path diff --git a/tests/nxpimage/data/bootable_image/rt6xx/fcb.bin b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/fcb.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt6xx/fcb.bin rename to tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/fcb.bin diff --git a/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keyblob.bin b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keyblob.bin new file mode 100644 index 0000000000000000000000000000000000000000..c86626638e0bc8cf47ca49bb1525b40e9737ee64 GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0 zEiNxGF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;0_J4r@ literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keystore.bin b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/keystore.bin new file mode 100644 index 0000000000000000000000000000000000000000..96922bbdc9c8ff668a02ed85e94faec71655a728 GIT binary patch literal 2048 zcmV+b2>Fh&!%lDG=x|%Xl?ESW2k&P3GYc&PYk38)y2pR30bm zE#8?+YgD4)27CY%KWl{|D%{)e0K|6SK}fkU5qY>^?4*%+pejtPC1sQMs5C7=L`~Cn z`-aRt0U8z&i@)3&Lu4x2C;WtQ@wPmoVm`J)F2pIdZ<*lJ=!GFR^T^vv9>3fg7~2x) zda{apy6A@#Weg)nN?l5Q4gjrrCW4S)hso*&zj`bVDPRZ+{QgE43!yktmTTw(^!BwQ zBntE}kT2`D5GcSjzp-cw&lpeQJ;SP>d7>>m9ZO$AALfH7GG0Uhi^hsCN?k>#nQJ9k zOwj;0U|Y-Iy0+gHLR*Sk@QvKd;NUtZ;1XL~blces3Uj>U(Z!H?I!n`5gr@24eUMMH zEHxjBIiu}9y{5QaaDi&h7FS?Q!6RePa+#$5qPrl?$67~TAumZ`@~Z~2$!-X>T29~i zgzp^Wic*t-&Ye^9Dn3#d2W-c%-k&xh>4H)AW*5uKpk`VrY!V;r#3{4QcU4(GOiYz< zc=rW?z9(dfx#d0j^dRVX_R3rjk&>ZO6z2LKG-*=2zYpGzUpGTy?zQ2Uq&b!KZt7*j%@gB+M5Lto>3@s`z?xCGH6zDyN|-WdI$vrO(ta^>Q8AYc zc66`Ob^^NOmM?>OSl1B9LR>0GuSs|f%goLY1P%&fr}{~B4E%cRSk`>ZjKIYF`!5!Q za7&a89mc#*M7mVY`l5I>)f_2-Z92iXvlPA0-qCnb+2Mfrf36>sg@>t$* z0woK`jDU=R3pEGwM`2KtMoF_`Sbiloc#)es4fH0~mGetkU1qge-AiAiwFa|- z@kzHThS4y~O*v?0Cews4LAbHK5hA)2B1^BiC{M`$ftjA3Q;d7FMun#>nCA!|?$~2a zr=gBqo*FYJgWyEke~)oF#48S+RsGJ10&+DYN|6t>bdWi_0tp)BG+8s>TtJTY>auPE$a#ae02-2b>pelu%)B*NLCv&1MUr^=+f(2RNJ5$k)veDnbLsR%QLUejI8o_w$2_~pYAK>3 z?;CUea-fR4@i@Qvy@}XhZ)k#_Io_jfio~=rSA2e8OajcOLsAKubU~rAjVp`A?GD;! zX&Fb3M)qhL-z9&&&ub^;29LE!?4EYy%vODXJ zwaeJx9E`ZTyapuf^W&)CJ}ZoKPo4`W-WimML*SlY@rDXcU=x_NkHA>$&XW_%^OPws zpThojND{uS{5W+RLE41-aWpp4XJz$^?htMGF-nN=YbKAx4OI5q>xtZfULG2+V`em- zqQf4u!bUfDTU|JoQW>ZaUj|KgDF8kQ47gAmIR5g(lvc;Cmc+G%&DxCd`0Om4=XmF| zo}RLF3~>GRhu=i;NkFJLeva2Zk`Y*<7v6RUxDCV->@?`tv#vBiCvo+>RS;WpeoM!K zGtC$8-B^)4?hq9p@Bl@mxbXKUu}o?7JgMI5kzByN#lW&SNnq!0z&uJOv9xoWIhm(a zn#%ZH_E#%;`qULI7$H+tQB5zC*Kut`TO!`&k<+4HPztUrS!;}C0&HK2#Qk5_bVNyp zKA6LLzJPQ_MHEw&jdO~`%jv$jS$^ITIYzQyclmhC?UHN$3hZ4YUP6C^4u0X|mXn)E z>@LrN@%anLq`t(ismsUdS?9-rZ{R2Kv%8HiDRNl~68rJGv83f7(FlA+X#^$K5WjD` ejSsi=(&yhu%I1&|Q#2vjymz|v(At94N4B5F&g@A5 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/bootable_image/rt6xx/merged_image.bin b/tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/merged_image.bin similarity index 100% rename from tests/nxpimage/data/bootable_image/rt6xx/merged_image.bin rename to tests/nxpimage/data/bootable_image/rt6xx/flexspi_nor/merged_image.bin diff --git a/tests/nxpimage/data/fcb/lpc55s3x/fcb_lpc55s3x_flexspi_nor.yaml b/tests/nxpimage/data/fcb/lpc55s3x/fcb_lpc55s3x_flexspi_nor.yaml index fd222b02..787bb4c2 100644 --- a/tests/nxpimage/data/fcb/lpc55s3x/fcb_lpc55s3x_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/lpc55s3x/fcb_lpc55s3x_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for lpc55s3x. # Created: 03/11/2022 11:55:53. # NXP SPSDK version: 1.8.1 diff --git a/tests/nxpimage/data/fcb/rt105x/fcb_rt105x_flexspi_nor.yaml b/tests/nxpimage/data/fcb/rt105x/fcb_rt105x_flexspi_nor.yaml index d42c1270..7efa5a63 100644 --- a/tests/nxpimage/data/fcb/rt105x/fcb_rt105x_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/rt105x/fcb_rt105x_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for rt105x. # Created: 23/05/2022 10:48:49. # NXP SPSDK version: 1.7.0 diff --git a/tests/nxpimage/data/fcb/rt106x/fcb_rt106x_flexspi_nor.yaml b/tests/nxpimage/data/fcb/rt106x/fcb_rt106x_flexspi_nor.yaml index e6a7b2c9..a1c6d5eb 100644 --- a/tests/nxpimage/data/fcb/rt106x/fcb_rt106x_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/rt106x/fcb_rt106x_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for rt106x. # Created: 23/05/2022 10:48:32. # NXP SPSDK version: 1.7.0 diff --git a/tests/nxpimage/data/fcb/rt117x/fcb_rt117x_flexspi_nor.yaml b/tests/nxpimage/data/fcb/rt117x/fcb_rt117x_flexspi_nor.yaml index 5af068b6..f24ab8a5 100644 --- a/tests/nxpimage/data/fcb/rt117x/fcb_rt117x_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/rt117x/fcb_rt117x_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for rt117x. # Created: 23/05/2022 10:47:19. # NXP SPSDK version: 1.7.0 diff --git a/tests/nxpimage/data/fcb/rt5xx/fcb_rt5xx_flexspi_nor.yaml b/tests/nxpimage/data/fcb/rt5xx/fcb_rt5xx_flexspi_nor.yaml index 257088d9..21388b59 100644 --- a/tests/nxpimage/data/fcb/rt5xx/fcb_rt5xx_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/rt5xx/fcb_rt5xx_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for rt5xx. # Created: 23/05/2022 10:47:19. # NXP SPSDK version: 1.7.0 diff --git a/tests/nxpimage/data/fcb/rt6xx/fcb_rt6xx_flexspi_nor.yaml b/tests/nxpimage/data/fcb/rt6xx/fcb_rt6xx_flexspi_nor.yaml index dd2ff8ca..559c357e 100644 --- a/tests/nxpimage/data/fcb/rt6xx/fcb_rt6xx_flexspi_nor.yaml +++ b/tests/nxpimage/data/fcb/rt6xx/fcb_rt6xx_flexspi_nor.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # FCB configuration for rt6xx. # Created: 23/05/2022 10:47:19. # NXP SPSDK version: 1.7.0 diff --git a/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bd b/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bd new file mode 100644 index 00000000..ef854793 --- /dev/null +++ b/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bd @@ -0,0 +1,14 @@ +options { + flags = 0x00; + startAddress = 0x30000000; + ivtOffset = 0x1000; + initialLoadSize = 0x2000; + entryPointAddress = 0x30002415; +} + +sources { + elfFile = extern(0); +} + +section (0) { +} \ No newline at end of file diff --git a/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bin b/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bin new file mode 100644 index 0000000000000000000000000000000000000000..7dce0509c08bba51a0ef37e278efea497808bd96 GIT binary patch literal 14644 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 Z0_Pn3zef+av=M0+KlkA7ng5N;e*j0!MSuVR literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.srec b/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.srec new file mode 100644 index 00000000..49086123 --- /dev/null +++ b/tests/nxpimage/data/hab/evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.srec @@ -0,0 +1,662 @@ +S3153000200000000420152400304124003045240030DF +S315300020103D2400303D2400303D24003000000000D7 +S3153000202000000000000000000000000049240030DD +S315300020303D240030000000004D2400304D3F00307C +S3153000204055240030592400305D240030612400309E +S3153000205065240030692400306D240030712400304E +S3153000206075240030792400307D24003081240030FE +S3153000207085240030892400308D24003091240030AE +S31530002080952400303D2400303D2400303D2400307E +S31530002090992400309D240030A1240030A52400303E +S315300020A0A9240030AD240030B1240030B5240030EE +S315300020B0B9240030BD240030C1240030C52400309E +S315300020C0C9240030CD240030D1240030D52400304E +S315300020D0D9240030DD240030E1240030E5240030FE +S315300020E0E9240030ED240030F1240030F5240030AE +S315300020F0F9240030FD24003001250030052500305C +S31530002100092500300D2500303D2400303D240030B7 +S315300021103D2400303D2400303D2400303D24003045 +S315300021203D2400303D2400303D2400303D24003035 +S315300021303D2400303D2400303D2400303D24003025 +S315300021403D2400303D2400303D2400303D24003015 +S315300021503D2400303D2400303D2400303D24003005 +S315300021603D2400303D2400303D2400301125003020 +S3153000217015250030192500301D2500302125003069 +S3153000218025250030292500302D2500303D2400300E +S315300021903D2400303D2400303D2400303D240030C5 +S315300021A03D2400303D2400303D2400303D240030B5 +S315300021B03D2400303D2400303D2400303D240030A5 +S315300021C03D240030312500303D2400303D240030A0 +S315300021D03D2400303D2400303D2400303D24003085 +S315300021E03D2400303D2400303D2400303D24003075 +S315300021F03D2400303D24003035250030392500306F +S315300022003D2400303D2400303D2400303D24003054 +S315300022103D2400303D2400303D2400303D24003044 +S315300022203D2400303D2400303D2400303D24003034 +S315300022303D2400303D2400303D2400303D24003024 +S315300022403D2400303D2400303D250030412500300E +S315300022503D24003045250030492500303D240030EE +S315300022603D2400304D2500305125003055250030B5 +S31530002270592500305D250030612500303D24003081 +S315300022803D2400303D2400303D2400303D240030D4 +S315300022903D2400303D2400303D2400303D240030C4 +S315300022A03D2400303D2400303D2400303D240030B4 +S315300022B03D2400303D2400303D2400303D240030A4 +S315300022C03D2400303D2400303D2400303D24003094 +S315300022D03D2400303D2400303D2400303D24003084 +S315300022E03D2400303D2400303D2400303D24003074 +S315300022F03D2400303D2400303D2400303D24003064 +S315300023003D2400303D2400303D2400303D24003053 +S315300023103D2400303D2400303D2400303D24003043 +S315300023203D2400303D2400303D2400303D24003033 +S315300023303D2400303D2400303D2400303D24003023 +S315300023403D2400303D2400303D2400303D24003013 +S315300023503D2400303D2400303D2400303D24003003 +S3153000236065250030692500306D2500307125003037 +S315300023703D2400303D2400303D2400303D240030E3 +S315300023803D2400303D24003075250030792500305D +S315300023907D250030812500303D2400303D2400303D +S315300023A03D2400303D2400303D2400303D240030B3 +S315300023B03D2400303D2400303D2400303D240030A3 +S315300023C03D2400303D2400303D2400303D24003093 +S315300023D03D2400303D2400303D2400303D24003083 +S315300023E03D2400303D2400303D2400303D24003073 +S315300023F03D2400303D2400303D240030FFFFFFFFF8 +S31530002400DFF80CD000F0C2F900480047A940003090 +S315300024100000042072B60548054901600A6882F357 +S3153000242008880448804762B60348004708ED00E054 +S3153000243000200030653F003001240030FFF7FEBF3A +S3153000244050480047504800475048004750480047DA +S3153000245050480047504800475048004750480047CA +S3153000246050480047504800475048004750480047BA +S3153000247050480047504800475048004750480047AA +S31530002480504800475048004750480047504800479A +S31530002490504800475048004750480047504800478A +S315300024A0504800475048004750480047504800477A +S315300024B0504800475048004750480047504800476A +S315300024C0504800475048004750480047504800475A +S315300024D0504800475048004750480047504800474A +S315300024E0504800475048004750480047504800473A +S315300024F0504800475048004750480047504800472A +S315300025005048004750480047504800475048004719 +S315300025105048004750480047504800475048004709 +S3153000252050480047504800475048004750480047F9 +S3153000253050480047504800475048004750480047E9 +S3153000254050480047504800475048004750480047D9 +S3153000255050480047504800475048004750480047C9 +S3153000256050480047504800475048004750480047B9 +S3153000257050480047504800475048004750480047A9 +S31530002580504800474124003045240030492400306B +S315300025904D2400304D3F00303D2400303D24003086 +S315300025A03D2400303D2400303D2400303D240030B1 +S315300025B03D2400303D2400303D2400303D240030A1 +S315300025C03D2400303D2400303D2400303D24003091 +S315300025D03D2400303D2400303D2400300D3900309C +S315300025E0253900303D390030553900306D390030ED +S315300025F0853900309D390030B5390030CD3900305D +S31530002600DD380030F53800303D2400303D240030D0 +S315300026103D2400303D2400303D2400303D24003040 +S315300026203D2400303D2400303D2400303D24003030 +S315300026303D2400303D2400303D2400303D24003020 +S315300026403D2400303D2400303D2400303D24003010 +S315300026503D2400303D2400303D2400303D24003000 +S315300026603D2400303D2400303D2400303D240030F0 +S315300026703D2400303D2400303D2400303D240030E0 +S315300026803D2400303D2400303D2400303D240030D0 +S315300026903D2400303D2400303D2400303D240030C0 +S315300026A03D2400303D2400303D2400303D240030B0 +S315300026B03D2400303D2400303D2400303D240030A0 +S315300026C03D2400303D2400302DE9F05F05460020E2 +S315300026D092469B4688460646814640241BE028465D +S315300026E041464746224600F041F853465A46C01AFC +S315300026F0914110D311461846224600F028F82D1A7B +S3153000270067EB01084F4622460120002100F01FF8F2 +S3153000271017EB00094E41201EA4F10104DFDC4846C8 +S3153000272031462A464346BDE8F09FD2B201E000F872 +S31530002730012B491EFBD270470022F6E710B513462F +S315300027400A4604461946FFF7F0FF204610BD202AF8 +S3153000275004DB203A00FA02F1002070479140C2F1C2 +S31530002760200320FA03F3194390407047202A04DBF4 +S31530002770203A21FA02F00021704721FA02F3D040C4 +S31530002780C2F120029140084319467047064C074D66 +S3153000279006E0E06840F0010394E8070098471034FB +S315300027A0AC42F6D3FFF730FE0C4900302C490030EE +S315300027B0B0B5062800F29380DFE800F004172D4804 +S315300027C06378840076480029D0F8204400F0898068 +S315300027D024F4803101F0A0FAC0F83034D0F800117A +S315300027E081F48031C0F8001179E06D480029D0F8C5 +S315300027F05044D0F8505400F0838025F4803101F0F5 +S3153000280068FAC0F8603401F0B4FAD0F85014614078 +S315300028108905FAD563E062480029D0F88044D0F8BB +S3153000282080547BD025F4803101F023FAC0F89034FF +S3153000283001F091FAD0F8801461408905FAD5D0F8C4 +S31530002840801421F48031C0F8801448E054480029BF +S31530002850D0F8B044D0F8B0546ED025F4803101F0C1 +S315300028602EFAC0F8C03401F07DFAD0F8B0146140C9 +S315300028708905FAD5D0F8B01421F48031C0F8B014F7 +S315300028802DE047480029D0F82045D0F8205561D0B2 +S3153000289025F4803101F036FAC0F8303501F0F3F91D +S315300028A0D0F8201561408905FAD518E03C48002952 +S315300028B0D0F8E0445CD001F0C4F9016881F40041FD +S315300028C001600CE036480029D0F8E0445BD001F0D6 +S315300028D0B8F9D0F8701181F00101C0F870110020FC +S315300028E0B0BD44F48031734601F016FA9E46D0F8F6 +S315300028F0001181F48031C0F80011D0F84004B0BD29 +S3153000290045F4803101F0E5F901F033FAD0F850148E +S3153000291061408905FAD5D0F87004B0BD45F48031F0 +S3153000292001F0A7F901F017FAD0F880146140890553 +S31530002930FAD5D0F8A004B0BD45F4803101F0BFF926 +S3153000294001F010FAD0F8B01461408905FAD5D0F804 +S31530002950D004B0BD45F4803101F0D4F901F093F9DB +S31530002960D0F8201561408905FAD5D0F84005B0BDBC +S31530002970734601F0E1F99E46016881F40041016039 +S31530002980D0F81005B0BD734601F0D6F99E46D0F8A2 +S31530002990701181F00101C0F87011D0F80005B0BD9A +S315300029A00044C8400A4600210023FFF701BF134602 +S315300029B00A460121FFF7FCBEF8B51C4615460E4601 +S315300029C00746FFF7EFFF0699A04305FA01F12140CC +S315300029D040EA010238463146BDE8F840FFF7E7BF26 +S315300029E00822002918BF042211464FF48032FFF71F +S315300029F0DEBF00002DE9F84398461746894604465F +S31530002A00DDE90856012802D0204601F0A1F94EB181 +S31530002A103088718841EA0040102140F40042204657 +S31530002A20FFF7C5FF204630212A46FFF7C0FF204674 +S31530002A3020213A46FFF7BBFF0020009000214A468E +S31530002A407F232046FFF7B8FF012C08D019200090CD +S31530002A50002142464FF060632046FFF7ADFF204627 +S31530002A60012100F011F82249642001F011FA2046C4 +S31530002A700121BDE8F84300F01FB800BF0822002945 +S31530002A8018BF042201F006B90822002918BF042213 +S31530002A9011464FF48002FFF78ABF0822002918BF7B +S31530002AA0042201F0F7B80822002918BF0422114683 +S31530002AB04FF40052FFF77BBF08234FF480420029C2 +S31530002AC018BF0423194618BF4FF4C042FFF76FBF33 +S31530002AD010B501210446FFF7E6FF0549E12001F074 +S31530002AE0D7F920460021BDE81040FFF7DCBF00BF14 +S31530002AF00046C3232DE9F84F00208DF80200ADF8CB +S31530002B0000000121134800F53A048346204601F0BF +S31530002B1037F92046012101F06FF9022000F070FDEF +S31530002B200D4CD4F8B00040F00200C4F8B00000F00C +S31530002B305BFD206C40F002002064206840F002000B +S31530002B402060206840F080702060206940F01400DA +S31530002B50206103E000400E401040C840206940002C +S31530002B60FCD56C46DFF8506400204FF48077ADF822 +S31530002B7001702146B04708202146B047DFF83C04B3 +S31530002B8000F0A2FB012001214FF0010800F04EFDBC +S31530002B9000F09AFADFF8280400F034FCDFF8245409 +S31530002BA0022000211B22A847022001211022A8471B +S31530002BB0022002211822A8470220032118224FF0B2 +S31530002BC00309A84700F094FC032000210D22A847F2 +S31530002BD0032001211122A847032002212022A847E1 +S31530002BE0032003211622A8470420012100F01EFDF0 +S31530002BF000F050FAF44800F0B9FC4FF48270ADF8AA +S31530002C00010021460020B0470220214640F2072A23 +S31530002C10ADF801A0B04740F20630ADF801002146CC +S31530002C200420B04705202146ADF80170B04740F288 +S31530002C300740ADF8010021460620B0474FF47040FA +S31530002C40ADF8010021460820B04709202146ADF8ED +S31530002C500170B0470A202146ADF80170B0470B200D +S31530002C602146ADF80170B0470C202146ADF8017011 +S31530002C70B0470D202146ADF80170B0470E202146F1 +S31530002C80ADF80170B0470F202146ADF80170B0475E +S31530002C9010202146ADF80170B04711202146ADF81D +S31530002CA00170B04712202146ADF80170B0471320AD +S31530002CB02146ADF80170B04715202146ADF80170B8 +S31530002CC0B04716202146ADF80170B047172021468F +S31530002CD0ADF80170B04718202146ADF80170B04705 +S31530002CE01920214641F20665ADF80150B0471A2049 +S31530002CF02146ADF80150B0471B202146ADF8017092 +S31530002D00B0471C202146ADF80170B0471D20214642 +S31530002D10ADF80170B0471E202146ADF80170B047BE +S31530002D201F202146ADF80170B04720202146ADF86E +S31530002D300170B04721202146ADF80170B0472220FE +S31530002D402146ADF80170B04723202146ADF8017019 +S31530002D50B04724202146ADF80170B04725202146E2 +S31530002D60ADF80170B04726202146ADF80170B04766 +S31530002D7027202146ADF80170B04728202146ADF80E +S31530002D800170B04729202146ADF80170B0472A209E +S31530002D902146ADF80170B0472B202146ADF80170C1 +S31530002DA0B0472C202146ADF80170B0472D20214682 +S31530002DB0ADF80170B0472E202146ADF80170B0470E +S31530002DC02F202146ADF80170B04730202146ADF8AE +S31530002DD00170B04731202146ADF80170B04732203E +S31530002DE02146ADF80170B04733202146ADF8017069 +S31530002DF0B04734202146ADF80170B0473620214621 +S31530002E00ADF80170B04737202146ADF80170B047B4 +S31530002E1038202146ADF80170B0473A202146ADF84A +S31530002E200170B0473B202146ADF80170B0473C20D9 +S31530002E302146ADF80170B0473D202146ADF801700E +S31530002E40B0473E202146ADF80170B0473F202146BD +S31530002E50ADF80170B04740202146ADF80170B0475B +S31530002E6041202146ADF80170B04742202146ADF8E9 +S31530002E700170B04743202146ADF80170B047442079 +S31530002E802146ADF801A0B04745202146ADF8017086 +S31530002E90B04746202146ADF80170B047472021465D +S31530002EA0ADF80170B04748202146ADF80170B04703 +S31530002EB049202146ADF80170B0474A202146ADF889 +S31530002EC00170B0474B202146ADF80170B0474C2019 +S31530002ED02146ADF80170B0474D202146ADF801705E +S31530002EE0B0474E202146ADF80170B047DBF8000000 +S31530002EF040F2FF41E74C20F00700CBF80000DBF84A +S31530002F00000069F3C500CBF80000DBF8000020F0C4 +S31530002F10C000CBF80000DBF8040020F00300CBF84B +S31530002F200400DBF8080020F00300CBF80800DBF8DB +S31530002F300C008843CBF80C002449DBF8100020F055 +S31530002F400100CBF81000DBF8140020F00100CBF8BC +S31530002F501400DBF8580020F00100CBF85800DBF8FD +S31530002F605C0020F00100CBF85C00DBF8600020F05C +S31530002F700100CBF86000DBF8640020F00100CBF8EC +S31530002F806400DBF8680020F00100CBF86800DBF85D +S31530002F906C0020F00100CBF86C002068C0F30220F2 +S31530002FA0085C00F0F7F8216858FA81F1B0FBF1F0CF +S31530002FB007490860BDE8F88F813600303841003067 +S31530002FC0EC48003075330030F8480030F9430030B3 +S31530002FD00000002010B5084C08492068C0F30220D4 +S31530002FE0084490F8C80000F0D5F82168012252FA5A +S31530002FF081F1B0FBF1F010BD800CCC408141003046 +S3153000300080B5FFF7E7FF034601204FF4E131012297 +S31530003010BDE8804000F04ABB1CB510480168C907BE +S3153000302005D101210160BFF34F8FBFF36F8F0C487D +S315300030300324817800888DF80610ADF8040009481D +S3153000304001AA032100F06AFB07480A210160C0F893 +S3153000305014410221C0F858131CBD00BF2066CC4075 +S31530003060324100300040C6401C810E40084A4301C0 +S3153000307001F00101D35803F001038B4206D042EA36 +S3153000308040100160BFF34F8FBFF36F8F704700BFA3 +S315300030900060CC4080B50B48016821F4005100F047 +S315300030A045FE02200021FFF7E9FC022000F050FE29 +S315300030B002200021FFF7F1FC02200021BDE880400C +S315300030C0FFF7E2BC0043C84080B50F48016821F4E1 +S315300030D000510160016821F000710160016821F042 +S315300030E0806100F023FE01200021FFF7C7FC01209C +S315300030F000F02EFE01200021FFF7CFFC0120002139 +S31530003100BDE88040FFF7C0BCC042C84000000000A8 +S3153000311070B52DED028B03241D4E042808BF022402 +S3153000312020460021B047054620463021B04700EE04 +S31530003130100A20462021B8EE408BB04700EE100A28 +S3153000314005F07F00012101EE100AC5F3426001FA55 +S3153000315000F0B8EE400BB8EE411B80EE080B30EEB7 +S31530003160010B9FED091B20EE010B01EE100AB8EEA4 +S31530003170411B80EE010BBCEEC00B10EE100ABDEC0D +S31530003180028B70BD00BF00BF0000000060E37641D7 +S31530003190A529003010B51E2800F25F80DFE800F068 +S315300031A0393B103D101012121D1D3F454A4F1F1F4F +S315300031B056585F6D642121696B232325252A2A0001 +S315300031C03D4810BD68483B4A01680068C9B2C0F343 +S315300031D0C13022FA00F0484310BD354810BD3248A0 +S315300031E010BD304810BD042000E00520BDE8104079 +S315300031F0FFF78EBF274C28492068C0F30220085CB1 +S31530003200FFF7C8FF2168012252FA81F1B0FBF1F0D5 +S3153000321010BD2C4810BD2A4810BD284810BD524854 +S31530003220006F10F03F000FD117E04F48006FC0F32A +S31530003230052008E04C48006FC0F3054003E04A48DB +S31530003240006FC0F3056040B119491DE0174810BD45 +S315300032504548006B10F03F0015D1002010BD4248A4 +S31530003260006BC0F305200CE03F48006BC0F30560EF +S3153000327007E00B4810BD094810BD3B48006BC0F352 +S3153000328005400028E9D00849B1FBF0F000EBC0005A +S31530003290400010BD0000CC407146003000C2EB0B40 +S315300032A00065CD1D00CA9A3B00389C1C001C4E0E92 +S315300032B000A4781F808D5B0000366E010084D7171E +S315300032C0006CDC020024F400B0B5EC4D04462868EE +S315300032D080041BD5286821790840884216D1207889 +S315300032E04FF4C03101EAC0302968014081420DD126 +S315300032F02868400403D4286840F480402860286851 +S3153000330040002FD5286820F080402AE0A5F50070CF +S3153000331000F0A0FD2868154910F4C04F0FBF20F00B +S31530003320005020F0602000F180402860084021786D +S31530003330227901F00301104440EAC1300C4940EAD9 +S3153000334001042C6085491E20BFF34F8FBFF36F8F6A +S3153000335000F09EFD28688000FCD524F0402000F562 +S3153000336080402860B0BD00BF0042C840005FFEBF4D +S3153000337000200040F8B59446032807D0022809D12A +S315300033801F4B03F1500003F1300205E01C4A02F1F5 +S31530003390100001E000220020CD003F26802406FAEE +S315300033A005F70668AC403E40EE40F3B2066863452A +S315300033B005D108232640CB4036FA03F31FD04023ED +S315300033C003FA05F60368334000930368234303602A +S315300033D00368BB4303600CF03F03AB4005682B43E7 +S315300033E00360022303FA01F1136859401160016842 +S315300033F0A14301600099026832409142FAD0F8BD8B +S315300034002042C8402DE9F041344D04462868000278 +S315300034103FD4A5F5107000F01DFD286B304940F003 +S315300034208030286388462868084204D02D490840F1 +S3153000343000F18040286074B1207A60B1606850B184 +S31530003440216821F0404129660188408840EA0140E0 +S3153000345040F400402862414C1E20234F2F60214605 +S3153000346000F016FDFA202146204E2E6000F010FDA9 +S3153000347007F50000286028688000FCD506F5C050A6 +S31530003480286008F108002860286B20F08030286317 +S315300034901CE09CB1207A296A090403D470B14FF438 +S315300034A0004000E0C00361680A8849886FF3DF3165 +S315300034B040EA02400843296A8842AAD12868800433 +S315300034C001D400F0ECFB2868400001D4BDE8F0815F +S315300034D0286820F080402860BDE8F0814042C8402E +S315300034E000208000FFDF7FBF0800004008088040D2 +S315300034F070B5184D286880021ED4A5F5047000F00A +S31530003500A9FC286A154C144E40F0803028622146BA +S315300035102E601E2000F0BCFC1148286021461E207B +S3153000352000F0B6FC06F50010286028688000FCD54F +S315300035300C4828600C480AE02868800401D400F062 +S31530003540AEFB2868400003D5286820F0804028600C +S3153000355070BD00BF1042C840100000400046C32373 +S315300035601008204018202040182020007CB51D4D22 +S315300035700446A5F5547000F06DFC032001212E6839 +S31530003580FFF72EFA06F02020B0F1202F0BD100F0F5 +S31530003590AAFB0320FFF772FA032000F0D9FB0320C1 +S315300035A00021FFF771FA00F07FFB01D0E46800E0FC +S315300035B00024CDE900040320FFF71CFA0320FFF7AF +S315300035C087FA28688000FCD503200121FFF756FAD8 +S315300035D02868002120F4804028600320BDE87C4024 +S315300035E0FFF7FEB95043C8400448016821F0010195 +S315300035F00160016941F00101016170475040C840E6 +S3153000360010B5044604203021FFF7CCF920F4A0622F +S31530003610012C03D024B942F4807201E042F4806276 +S3153000362004203021BDE81040FFF7C1B905280BD87A +S31530003630DFE800F0030B0E161E211048026822F454 +S31530003640003261F35142026070470120FFF7C8B97A +S315300036500A48026C22F4803261F3104202647047E9 +S315300036600648026922F4803261F3104202617047E3 +S315300036700220FFF7B5B90320FFF7B2B90042C840C0 +S315300036800A784B7889780139C9B2002A064A63F339 +S315300036900A2118BF01F18071C0011150BFF34F8F5D +S315300036A0BFF36F8F704700BF0000CC4010B588B0B5 +S315300036B08DF80E00002048F64174ADF80C00ADF8D8 +S315300036C01A408DF81820012A12480290CDE90031AF +S315300036D01CD16846104C07900021202204F10800C6 +S315300036E0FFF72CF804F10C00A06004A900F0E2FB0F +S315300036F0207804F12001A06800F00AFCA06804F1EB +S31530003700240100F0ECFBA168616001E043F2215036 +S3153000371008B010BD0000010108000020B0B500233C +S3153000372019A4102B17D054F82350854201D00133F9 +S31530003730F7E70D2B0FD873B123A4E35C264C5D015C +S315300037406559ED0707D144EA431301241C60BFF3E2 +S315300037504F8FBFF36F8F012344698B409C43446185 +S31530003760147844B154788425002C08BF88254351F9 +S315300037704468234302E0DB43446823404360927845 +S31530003780BDE8B04000F02AB80000000000C012408A +S3153000379000001340004013400080134000C0134027 +S315300037A00000144000C0C5400000C6400040C6407E +S315300037B00080C64000C0C6400000C7400000CA4076 +S315300037C00080004200C000428A3333333333333310 +S315300037D033333333333300000060CC40B0B501238C +S315300037E003FA01F4C369A343C3611023102934BF1C +S315300037F00C231039013A042A25D8DFE802F0030BEE +S315300038001219210049000324C25804FA01F122EAB0 +S31530003810010113E049000324C2588C40A24301241D +S3153000382009E049000324C2588C40A243022402E036 +S31530003830C2584900032404FA01F11143C150B0BD06 +S31530003840C1692143C161B0BD8423002A08BF8823E2 +S31530003850012202FA01F1C1507047000070B586B0FE +S3153000386001AE04460D46304600F0C0F840F2011075 +S31530003870ADF812001849D5E900200190287A8DF864 +S315300038800800687A8DF80B00A87A002818BF012046 +S315300038908DF81500E87A002818BF01208DF814003D +S315300038A0287B002818BF01208DF80E00687B002881 +S315300038B018BF01208DF80F00A87B51F82000314643 +S315300038C000F0CAF810B142F6AF7002E0A87B207063 +S315300038D0002006B070BD00BF4041003080B5404882 +S315300038E0816A404802680248904700F0A7B900BF95 +S315300038F000000A4080B53A48C16A3A480268024830 +S31530003900904700F09BB900BF0040C24080B53448B4 +S315300039104168344802680248904700F08FB900BFCA +S3153000392000C0074080B52E4881682E48026802489C +S31530003930904700F083B900BF0000084080B52848A2 +S31530003940C168284802680248904700F077B900BF3E +S315300039500040084080B52248016922480268024882 +S31530003960904700F06BB900BF0080084080B51C4816 +S3153000397041691C4802680248904700F05FB900BFB1 +S3153000398000C0084080B5164881691648026802486A +S31530003990904700F053B900BF0000094080B5104889 +S315300039A0C169104802680248904700F047B900BF25 +S315300039B00040094080B50A48016A0A480268024850 +S315300039C0904700F03BB900BF0080094080B50448FD +S315300039D0416A044802680448904700F02FB900BF96 +S315300039E0380000200400002000C00940B0B5002196 +S315300039F0142204460025FEF7A1FE4FF4E130C4E957 +S31530003A000005A560C4F80E50A581B0BD0146002062 +S31530003A1004A200E001300D281CBF52F820308B4242 +S31530003A20F8D170470000000000C007400000084091 +S31530003A30004008400080084000C0084000000940AF +S31530003A40004009400080094000C0094000000A409B +S31530003A500040C2400080C2402DE9F84F814602EB5B +S31530003A6082000C6888464FF0000A04254FF00A0C95 +S31530003A704FF0000B4FEA400EA1002046212D16D004 +S31530003A80BEFBF1F321440533B3FBFCF31E0408BF40 +S31530003A9001239EB26E43B2FBF6F6A71B38BF371B27 +S31530003AA087429EBF9B46AA4638460135E6E76421E3 +S31530003AB0B4FBF1F101EB4101884203D940F22150C8 +S31530003AC0BDE8F88F4846FFF7A1FF5949085C012148 +S31530003AD0FFF7CCFAD9F808006FF35F3B40F00200ED +S31530003AE0C9F80800D9F8080020F00200C9F8080023 +S31530003AF00AF0FC00D9F81010042808BF41F4003150 +S31530003B004C4808404FF0F85101EB0A6101F0F8518A +S31530003B1059440844C9F8100040F21771D9F810001A +S31530003B2020F00050C9F81000D9F8180098F80F4066 +S31530003B3098F80E3098F80520884398F8041004F069 +S31530003B40070403F00103084340EA0420012A40EA4F +S31530003B50830006D100290CBF40F4006020F40060D9 +S31530003B6003E0002918BF40F01000C9F8180098F893 +S31530003B70092098F8070098F8081098F80A30D9F80C +S31530003B80104060F34D34C9F8104041EA0240C9F89C +S31530003B902C00D9F8280040F08800C9F82800D9F858 +S31530003BA0280040F44040C9F8280098F80D00D9F8AC +S31530003BB0241000F0010041EA001098F80C1001F0D2 +S31530003BC0010140EA4110C9F824002BB1D9F824008C +S31530003BD040F00800C9F8240098F80B0028B1D9F84D +S31530003BE0240040F00100C9F82400134B98F8060071 +S31530003BF098F8101098F8112003F10053002804BFEC +S31530003C004CF20003CCF21F03D9F814001843C9F85C +S31530003C1014000029D9F8180018BF40F40020002AF3 +S31530003C2018BF40F48020C9F818000020BDE8F88F8E +S31530003C307441003000E0FFE000C01FC0BFF34F8F7B +S31530003C4080BD24F48031C0F8E014D0F8E01421F0BF +S31530003C50FF011144C0F8E014C0F8F0347047016039 +S31530003C60BFF34F8FBFF36F8F70474FF47A7000F00A +S31530003C700FB9C0F88014D0F8801421F0FF01114438 +S31530003C80C0F880147047D0F8201581F48071C0F8E0 +S31530003C902015704711464FF40042FEF788BE28685B +S31530003CA040F4005028607047286840F4005028607F +S31530003CB0267C21786378D4E90120002E7047C0F83D +S31530003CC0B014D0F8B01421F0FF011144C0F8B0148C +S31530003CD07047C0F85014D0F8501421F0FF01114449 +S31530003CE0C0F8501470472868002120F4005028602E +S31530003CF0286840F4804028607047C4F810054FF4B7 +S31530003D007A703047C0F82015D0F8201521F0FF0121 +S31530003D101144C0F820157047C0F82014D0F820148C +S31530003D2021F0FF011144C0F820147047016001688A +S31530003D3041F480410160704744F48031C0F8E014AA +S31530003D40D0F8E01421F0FF011144C0F8E0147047B8 +S31530003D500021FEF7B1BED0F8801481F48071C0F82E +S31530003D6080147047D0F8B01481F48071C0F8B01464 +S31530003D707047D0F8501481F48071C0F850147047F1 +S31530003D8070B50446D0F81005404DD9B120F0010089 +S31530003D90C4F81005174E29464FF47A70B047D4F858 +S31530003DA01005294640F40020FFF7A7FFD4F8100588 +S31530003DB0294640F02000FFF7A0FFD4F8100540F068 +S31530003DC0040013E020F00500C4F810052946FFF77B +S31530003DD04CFFD4F81005294620F02000C4F8100511 +S31530003DE0FFF743FFD4F8100520F40020C4F810057F +S31530003DF070BD00BF913E003070B50446D0F8300536 +S31530003E00224D69B140F40030C4F830052946FFF739 +S31530003E102CFFD4F830056FF0040640F480200CE017 +S31530003E2040F00400C4F830052946FFF71EFFD4F8E9 +S31530003E3030056FF4003620F48020C4F830054FF496 +S31530003E407A70294600F024F8D4F830053040C4F8AA +S31530003E50300570BD10B5044600200021FEF7A2FDE6 +S31530003E6040F2051188420FD00020002140F20512A1 +S31530003E70FEF79DFD0549012000F00AF8D4F87005DB +S31530003E8040F01000C4F8700510BD00BF0046C323D3 +S31530003E9060B180B5A1FB0001054A0023FEF714FC92 +S31530003EA040000046401E0028FCD180BD704700BF50 +S31530003EB0C0C62D00B0B50C46002114220546FEF7CB +S31530003EC03DFC207A287001281CBF43F22150B0BD3A +S31530003ED005F10C00E168BDE8B04000F02EB8F8B549 +S31530003EE00646EFF31087EFF3108072B6706810B1A4 +S31530003EF043F2225408E00D46716008460021042240 +S31530003F000024FEF71BFC2E6087F310882046F8BD90 +S31530003F10B0B505460C46EFF31080EFF3108172B65C +S31530003F200422A9680131A96080F310882046002157 +S31530003F30FEF704FC00202560B0BDFFF78FBC000003 +S31530003F400649086008680028FCD1704703480168B4 +S31530003F5011B1016801390160704700BF340000209B +S31530003F6070470000F0B54848416F41F47001416731 +S31530003F7046490A88520703D50A8822F004020A8085 +S31530003F8043490A88520703D50A8822F004020A8078 +S31530003F90404A1168890440490B4604D44CF22053F8 +S31530003FA053604DF6281353604FF6FF7393601468D1 +S31530003FB044F0200424F080041460384A1468A404C1 +S31530003FC004D44CF2205151604DF628115160936063 +S31530003FD0116841F0200121F08001116049490A68D9 +S31530003FE0D20703D00A6822F001020A600168890309 +S31530003FF012D4016889030FD4BFF34F8F0021BFF36A +S315300040006F8FC0F83C12BFF34F8FBFF36F8F0168CD +S3153000401041F40031FFF723FE0168C90326D4016855 +S31530004020C90323D40021016743F6E07CBFF34F8FE9 +S31530004030C26EC2F3C903C2F34E319A0701330CEA9A +S31530004040411516461F4646EA050406F14046013F2D +S31530004050C0F84C42F7D14C1E00292146EFD1BFF3B0 +S315300040604F8F016841F48031FFF7F9FD0C490A683A +S3153000407022F400520A60016841F01001FFF7EFFDAB +S31530004080BDE8F04000F00EB814ED00E0000003404B +S31530004090004003400080034020C528D90000C140BD +S315300040A070400E4070470000FEF7B6FFFEF722FD67 +S315300040B0FEF7A6FFFFF754FF11484FF47A710068F8 +S315300040C0B0FBF1F00138B0F1807F00D3FEE70D4947 +S315300040D0F0220E4E0E4C48600B480270002088606D +S315300040E0072008604FF47A70FFF72AFF30780321F3 +S315300040F080F0010520462A46FFF7A6FB3570F1E72A +S315300041000000002010E000E023ED00E03000002049 +S315300041100040C64002E008C8121F08C1002AFAD182 +S3153000412070477047002001E001C1121F002AFBD101 +S31530004130704701000000000001000000C8000000C8 +S315300041400000000000C00740000008400040084062 +S315300041500080084000C00840000009400040094087 +S315300041600080094000C0094000000A400040C240BB +S315300041700080C2408A565758595A5B5C5D5E5F6014 +S31530004180610205030007FF0FFF02050300140F0944 +S3153000419018020503000F18090D02050300140F0954 +S315300041A0180205030018090B11020503001018093F +S315300041B00D0205030010180B0902050300140F0A3F +S315300041C018020503000910180A0205030010180921 +S315300041D00D020503001018090D020503000F181A09 +S315300041E00D020503001018090D0205030010180909 +S315300041F00D02050300101813140205030010181AD7 +S315300042001C0205030010181A1C02050300101813AF +S3153000421014020503001018131402050300101813B6 +S31530004220140205030011090C0F0205030011090CD5 +S315300042300F020503001018090D02050300101809B6 +S315300042400D02050300140F0D18020503001018099E +S315300042500D020503001018090D0205030010180998 +S315300042600D020503001018090D0205030010180988 +S315300042700D020503001018090D0205030010180978 +S315300042800D020503001018090D0205030010180968 +S315300042900D020503001018090D02050300140F0D59 +S315300042A01802050300140F0D180205030010180933 +S315300042B00D020503001018090D0205030010180938 +S315300042C00D020503001018090D02050300140F0D29 +S315300042D01802050300140F0D180205030013180900 +S315300042E00D020503001318090D0205030013180902 +S315300042F00D020503001318090D02050300140F13F0 +S315300043001802050300140F131802050300101809CC +S315300043100D020503001018090D02050300171A18BF +S315300043200B02050300171A180BFFFFFFFFFFFFFFF5 +S31530004330FF02050300171A180B02050300171A1897 +S315300043400B02050300171A180BFFFFFFFFFFFFFFD5 +S31530004350FF020503000C0A1807020503000C0A18B1 +S31530004360070205030018101A0D0205030018101A6B +S315300043700D02050300140F1A18020503001A0F1355 +S315300043800D020503001A13180D020503001A13183F +S315300043900D020503001A13180D02050300140F1A37 +S315300043A01802050300090B0F1C02050300090C1146 +S315300043B01C02050300090C111C02050300090A1131 +S315300043C01C02050300090A111C020503000C0F0A22 +S315300043D01C020503000C0F0A1C020503000C0F0A11 +S315300043E01C020503000C0F121C020503000C0912F7 +S315300043F018020503000D01121A0205030007FF0F0C +S31530004400FF02050300140F0918020503000F1809EF +S315300044100D02050300140F09180205030018090BD5 +S3153000442011020503001018090D0205030010180BC0 +S315300044300902050300140F0A1802050300091018B3 +S315300044400A020503001018090D02050300101809A9 +S315300044500D020503000F181A0D0205030010180986 +S315300044600D020503001018090D020503001018137C +S31530004470140205030010181A1C0205030010181A3E +S315300044801C0205030010181314020503001018133C +S315300044901402050300101813140205030011090C49 +S315300044A00F0205030011090C0F020503001018094D +S315300044B00D020503001018090D02050300140F0D37 +S315300044C018020503001018090D020503001018091B +S315300044D00D020503001018090D0205030010180916 +S315300044E00D020503001018090D0205030010180906 +S315300044F00D020503001018090D02050300101809F6 +S315300045000D020503001018090D02050300101809E5 +S315300045100D02050300140F0D1802050300140F0DCC +S3153000452018020503001018090D02050300101809BA +S315300045300D020503001018090D02050300101809B5 +S315300045400D02050300140F0D1802050300140F0D9C +S3153000455018020503001318090D0205030013180984 +S315300045600D020503001318090D020503001318097F +S315300045700D02050300140F131802050300140F1360 +S3153000458018020503001018090D020503001018095A +S315300045900D02050300171A180B02050300171A1827 +S315300045A00BFFFFFFFFFFFFFFFF02050300171A187F +S315300045B00B02050300171A180B02050300171A1809 +S315300045C00BFFFFFFFFFFFFFFFF020503000C0A187A +S315300045D007020503000C0A18070205030018101A13 +S315300045E00D0205030018101A0D02050300140F1AE8 +S315300045F018020503001A0F130D020503001A1318CB +S315300046000D020503001A13180D020503001A1318BC +S315300046100D02050300140F1A1802050300090B0FCB +S315300046201C02050300090C111C02050300090C11BC +S315300046301C02050300090A111C02050300090A11B0 +S315300046401C020503000C0F0A1C020503000C0F0A9E +S315300046501C020503000C0F0A1C020503000C0F1286 +S315300046601C020503000C091218020503000D011285 +S315300046701A0205030007FF0FFF02050300140F0996 +S3153000468018020503000F18090D02050300140F095F +S31530004690180205030018090B11020503001018094A +S315300046A00D0205030010180B0902050300140F0A4A +S315300046B018020503000910180A020503001018092C +S315300046C00D020503001018090D020503000F181A14 +S315300046D00D020503001018090D0205030010180914 +S315300046E00D02050300101813140205030010181AE2 +S315300046F01C0205030010181A1C02050300101813BB +S3153000470014020503001018131402050300101813C1 +S31530004710140205030011090C0F0205030011090CE0 +S315300047200F020503001018090D02050300101809C1 +S315300047300D02050300140F0D1802050300101809A9 +S315300047400D020503001018090D02050300101809A3 +S315300047500D020503001018090D0205030010180993 +S315300047600D020503001018090D0205030010180983 +S315300047700D020503001018090D0205030010180973 +S315300047800D020503001018090D02050300140F0D64 +S315300047901802050300140F0D18020503001018093E +S315300047A00D020503001018090D0205030010180943 +S315300047B00D020503001018090D02050300140F0D34 +S315300047C01802050300140F0D18020503001318090B +S315300047D00D020503001318090D020503001318090D +S315300047E00D020503001318090D02050300140F13FB +S315300047F01802050300140F131802050300101809D8 +S315300048000D020503001018090D02050300171A18CA +S315300048100B02050300171A180BFFFFFFFFFFFFFF00 +S31530004820FF02050300171A180B02050300171A18A2 +S315300048300B02050300171A180BFFFFFFFFFFFFFFE0 +S31530004840FF020503000C0A1807020503000C0A18BC +S31530004850070205030018101A0D0205030018101A76 +S315300048600D02050300140F1A18020503001A0F1360 +S315300048700D020503001A13180D020503001A13184A +S315300048800D020503001A13180D02050300140F1A42 +S315300048901802050300090B0F1C02050300090C1151 +S315300048A01C02050300090C111C02050300090A113C +S315300048B01C02050300090A111C020503000C0F0A2D +S315300048C01C020503000C0F0A1C020503000C0F0A1C +S315300048D01C020503000C0F121C020503000C091202 +S315300048E018020503000D01121A000000FFFFFF0F2A +S315300048F00000000000000000290000000100000058 +S3153000490000A60E0000000000000000002C49003018 +S315300049100000002008000000144100303449003007 +S3153000492008000020640000002441003000A4781FF5 +S309300049303D240030BC +S503029466 +S70530002401A5 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bd b/tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bd new file mode 100644 index 00000000..8e8ef96f --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bd @@ -0,0 +1,15 @@ +options { + flags = 0x00; + startAddress = 0x2024ff00; + ivtOffset = 0x0; + initialLoadSize = 0x100; + + entryPointAddress = 0x202629e1; +} + +sources { + elfFile = extern(0); +} + +section (0) { +} diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bin b/tests/nxpimage/data/hab/evkmimxrt1170_flashloader.bin new file mode 100644 index 0000000000000000000000000000000000000000..9b33cd145a309bb74f74508746d07aa03fb03c9e GIT binary patch literal 82431 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
&9_v~?^|lL%1fEF(@RsAs?+RMvrDt9 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 AGynhq literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.s19 b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.s19 new file mode 100644 index 00000000..9bfcd58d --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.s19 @@ -0,0 +1,1418 @@ +S02B000065766B6D696D787274313137305F696C65645F626C696E6B795F636D375F515350495F464C41534870 +S3153000200000000420E12400302D2500300F5F003021 +S3153000201031250030332500303525003000000000F2 +S3153000202000000000000000000000000037250030EE +S3153000203039250030000000003B250030A92800304B +S315300020403F580030475800304F580030575800300E +S315300020505F580030675800306F580030775800307E +S315300020607F580030875800308F58003097580030EE +S315300020709F580030A7580030AF580030B75800305E +S31530002080BF580030C7580030CF580030D7580030CE +S31530002090DF580030E7580030EF580030F75800303E +S315300020A0FF580030075900300F59003017590030AB +S315300020B01F590030275900302F590030375900301A +S315300020C03F590030475900304F590030575900308A +S315300020D05F590030675900306F59003077590030FA +S315300020E07F590030875900308F590030975900306A +S315300020F09F590030A7590030AF590030B7590030DA +S31530002100BF590030C7590030CF590030D759003049 +S31530002110DF590030E7590030EF590030F7590030B9 +S31530002120FF590030075A00300F5A0030175A003026 +S315300021301F5A0030275A00302F5A0030375A003095 +S315300021403F5A0030475A00304F5A0030575A003005 +S315300021505F5A0030675A00306F5A0030775A003075 +S315300021607F5A0030875A00308F5A0030975A0030E5 +S315300021709F5A0030A75A0030AF5A0030B75A003055 +S31530002180BF5A0030C75A0030CF5A0030D75A0030C5 +S31530002190DF5A0030E75A0030EF5A0030F75A003035 +S315300021A0FF5A0030075B00300F5B0030175B0030A2 +S315300021B01F5B0030275B00302F5B0030375B003011 +S315300021C03F5B0030475B00304F5B0030575B003081 +S315300021D05F5B0030675B00306F5B0030775B0030F1 +S315300021E07F5B0030875B00308F5B0030975B003061 +S315300021F09F5B0030A75B0030AF5B0030B75B0030D1 +S31530002200BF5B0030C75B0030CF5B0030D75B003040 +S31530002210DF5B0030E75B0030EF5B0030F75B0030B0 +S31530002220FF5B0030075C00300F5C0030175C00301D +S315300022301F5C0030275C00302F5C0030375C00308C +S315300022403F5C0030475C00304F5C0030575C0030FC +S315300022505F5C0030675C00306F5C0030775C00306C +S315300022607F5C0030875C00308F5C0030975C0030DC +S315300022709F5C0030A75C0030AF5C0030B75C00304C +S31530002280BF5C0030C75C0030CF5C0030D75C0030BC +S31530002290DF5C0030E75C0030EF5C0030F75C00302C +S315300022A0FF5C0030075D00300F5D0030175D003099 +S315300022B01F5D0030275D00302F5D0030375D003008 +S315300022C03F5D0030475D00304F5D0030575D003078 +S315300022D05F5D0030675D00306F5D0030775D0030E8 +S315300022E07F5D0030875D00308F5D0030975D003058 +S315300022F09F5D0030A75D0030AF5D0030B75D0030C8 +S31530002300BF5D0030C75D0030CF5D0030D75D003037 +S31530002310DF5D0030E75D0030EF5D0030F75D0030A7 +S31530002320FF5D0030075E00300F5E0030175E003014 +S315300023301F5E0030275E00302F5E0030375E003083 +S315300023403F5E0030475E00304F5E0030575E0030F3 +S315300023505F5E0030675E00306F5E0030775E003063 +S315300023607F5E0030875E00308F5E0030975E0030D3 +S315300023709F5E0030A75E0030AF5E0030B75E003043 +S31530002380BF5E0030C75E0030CF5E0030D75E0030B3 +S31530002390DF5E0030E75E0030EF5E0030F75E003023 +S315300023A0FF5E0030075F003070780030000000209C +S315300023B004000000707800300000000000000000CB +S315300023C07078003000002420000000007078003063 +S315300023D000002C200000000070780030000034200F +S315300023E0000000007078003000003520000000004A +S315300023F070780030000000800000000070780030F7 +S3153000240000000083000000000400002018010000D6 +S315300024100000000000000000000024200000000042 +S3153000242000002C20000000000000342000000000D6 +S315300024300000352000000000000000800000000091 +S31530002440000000830000000030B40B4600249442A4 +S3153000245005D250F8045B43F8045B0434F7E70C4BC1 +S315300024605B6913F4803F11D0002A0FDD01F01F03A2 +S315300024701A44BFF34F8F064BC3F868122031203A07 +S31530002480002AF8DCBFF34F8FBFF36F8F30BC704735 +S3153000249000ED00E010B4034600228A4204D2002444 +S315300024A043F8044B0432F8E70C4B5B6913F4803F76 +S315300024B011D000290FDD00F01F031944BFF34F8FF1 +S315300024C0064BC3F86802203020390029F8DCBFF308 +S315300024D04F8FBFF36F8F5DF8044B704700ED00E010 +S315300024E010B572B602F000F90D4B07E003F10C049B +S315300024F09A6859681868FFF7A7FF2346094A934236 +S31530002500F4D306E01C46596854F8080BFFF7C2FFAF +S315300025102346054A9342F5D362B603F069FFFEE7D8 +S31530002520A82300300824003048240030FEE7FEE7B8 +S31530002530FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE73D +S3153000254080B58AB000AF0346FB7100237B6297F9F2 +S315300025500730042B06D097F90730052B02D03248C6 +S3153000256000F064F897F90730042B01D1022300E01C +S3153000257003230021184603F0F2FC38623B6A5B0EF7 +S3153000258003F00703FB613B6A03F07F033B6297F975 +S315300025900730042B01D1022300E0032330211846F3 +S315300025A003F0DDFC07EE900AB8EE677B87ED047B1F +S315300025B097F90730042B01D1022300E003232021B1 +S315300025C0184603F0CCFC07EE900AB8EE677B87ED31 +S315300025D0027B3B6A07EE903AB8EE676B97ED024B9B +S315300025E097ED045B84EE057B36EE077B9FED0C6B37 +S315300025F027EE065B0122FB6902FA03F307EE903AF7 +S31530002600B8EEE76B85EE067BFCEEC77B17EE903AAD +S315300026107B627B6A18462837BD4680BDAFF30080A3 +S315300026200000000060E37641006D003080B582B076 +S3153000263000AF78607968024800F0AEF800BEFDE77A +S315300026406C68003080B584B000AF786039607B68E4 +S31530002650002BFCD03B68002BFCD00C4B1B68002BAE +S3153000266001D100230DE03A687968094803F008FE85 +S315300026700346FB81FB89002B01D13B6801E04FF01B +S31530002680FF3318461037BD4680BD00BF20000020FE +S315300026901800002080B58EB000AFB9607B6003466D +S315300026A0FB731346BB730023FB867B687B61BB6879 +S315300026B0BB6100233B7700237B770123BB77012364 +S315300026C0FB77FB7B87F82030BB7B87F82C3048F6CE +S315300026D04173FB85BB7B012B03D107F114033B63AD +S315300026E017E0BB7B022B03D143F22153FB8610E06C +S315300026F0BB7B032B03D143F22153FB8609E0BB7B23 +S31530002700042B03D143F22153FB8602E043F22153DB +S31530002710FB86FB8E43F2215293422ED01C2200219F +S31530002720184803F06EFE174B174A1A60154B1B6894 +S3153000273007F124021146184603F01AFD0346FB86BC +S31530002740FB8E002BFCD10F4B1B681049184603F04B +S315300027503FFD0346FB86FB8E002BFCD1094B1B68E5 +S315300027600B49184603F05CFD0346FB86FB8E002BB7 +S31530002770FCD1044B1B68074A1360FB8E184638376A +S31530002780BD4680BD0400002008000020180000204F +S315300027901C000020200000200FB480B5A4B000AF8C +S315300027A00023C7F888300023C7F88C3000237B60BD +S315300027B007F108037C220021184603F022FE124B53 +S315300027C01B68002B16D007F19C03C7F884303A1DDE +S315300027D00E4BD7F88410D7F8980002F0E8FEC7F809 +S315300027E08800D7F888203B1D11461846FFF72AFF88 +S315300027F0C7F88C00D7F88C3018469037BD46BDE800 +S31530002800804004B0704700BF200000208151003066 +S3153000281080B483B000AF03463960FB80B7F9063029 +S31530002820002B0ADB3B68DAB20C49B7F906301201E5 +S31530002830D2B20B4483F800230AE03B68DAB2084987 +S31530002840FB8803F00F03043B1201D2B20B441A7615 +S3153000285000BF0C37BD465DF8047B704700E100E0F1 +S3153000286000ED00E080B582B000AF78607B68013B58 +S31530002870B3F1807F01D301230FE00A4A7B68013B25 +S3153000288053600F214FF0FF30FFF7C2FF054B002298 +S315300028909A60044B07221A60002318460837BD4653 +S315300028A080BD00BF10E000E080B400AF064B1B686F +S315300028B0002B04D0044B1B68013B034A136000BF56 +S315300028C0BD465DF8047B70472400002080B483B099 +S315300028D000AF7860074A7B68136000BF054B1B6802 +S315300028E0002BFBD100BF00BF0C37BD465DF8047B23 +S315300028F0704700BF2400002080B500AF02F0E4FA34 +S3153000290002F0BEF903F00FFD03F0B1FB134B1B6869 +S31530002910134AA2FB03239B091846FFF7A3FF03467E +S31530002920002B00D0FEE74FF47A70FFF7CFFF0D4B48 +S315300029301B78DBB2002B08D0002203210A4801F0B5 +S3153000294093F9084B00221A70EDE70122032106485D +S3153000295001F08AF9034B01221A70E4E700000020E7 +S31530002960D34D6210180100200040C64080B582B0B9 +S3153000297000AF0021002003F0F2FA78607B6840F265 +S315300029800512934222D040F205120021002003F0B6 +S31530002990D4FA0021002003F0E2FA0E4B4FF4807295 +S315300029A0C3F880250B4BD3F820380A4A23F48033FA +S315300029B0C2F820380849012001F064F8054BD3F8F5 +S315300029C07035044A43F01003C2F8703500BF08373B +S315300029D0BD4680BD0040C84000CA9A3B80B586B02F +S315300029E000AF3B600346FB710B46BB7113467B71F0 +S315300029F0FB79062B00F2008301A252F823F000BFC8 +S31530002A001D2A0030C92A0030B52B0030B52C0030D5 +S31530002A10B12D00309D2E00304D2F0030BB79002B6C +S31530002A2024D0A34BD3F82038A14A23F48033C2F8FC +S31530002A3020389F4BD3F820387B617B6923F0FF0326 +S31530002A407B617B797A6913437B61994A7B69C2F8EA +S31530002A502038974A3B68C2F83038954BD3F8003562 +S31530002A60934A83F48033C2F80035C8E2904BD3F8EA +S31530002A7020387B617B6923F480337B617B6943F447 +S31530002A8080337B618A4A7B69C2F82038884BD3F819 +S31530002A9020387B617B6923F0FF037B617B797A6920 +S31530002AA013437B61824A7B69C2F82038804BD3F866 +S31530002AB000357F4A83F48033C2F800357C4BD3F837 +S31530002AC040383B613B699BE2BB79002B34D0784B75 +S31530002AD0D3F8503803F40073FB60754BD3F8503895 +S31530002AE0734A23F48033C2F85038714BD3F85038D8 +S31530002AF07B617B6923F0FF037B617B797A691343C2 +S31530002B007B616B4A7B69C2F85038694A3B68C2F8C8 +S31530002B106038674BD3F85038654A83F48073C2F80F +S31530002B205038634BD3F8503803F40073BB60BA683F +S31530002B30FB689A42F5D062E25D4BD3F8503803F425 +S31530002B400073FB605A4BD3F850387B617B6923F4B2 +S31530002B5080337B617B6943F480337B61544A7B6984 +S31530002B60C2F85038524BD3F850387B617B6923F02A +S31530002B70FF037B617B797A6913437B614C4A7B69BE +S31530002B80C2F850384A4BD3F85038494A83F48073E8 +S31530002B90C2F85038464BD3F8503803F40073BB6054 +S31530002BA0BA68FB689A42F5D0414BD3F870383B612E +S31530002BB03B6925E2BB79002B3CD03D4BD3F88038BE +S31530002BC003F40073FB603A4BD3F88038384A23F469 +S31530002BD08033C2F88038364BD3F880387B617B69D6 +S31530002BE023F0FF037B617B797A6913437B61304A3B +S31530002BF07B69C2F880382E4A3B68C2F890382C4B35 +S31530002C00D3F880382A4A83F48073C2F88038284B48 +S31530002C10D3F8803803F40073BB60BA68FB689A4215 +S31530002C20F5D0234BD3F88038214A23F48033C2F8C9 +S31530002C308038E4E11E4BD3F8803803F40073FB6030 +S31530002C401B4BD3F880387B617B6923F480337B61FF +S31530002C507B6943F480337B61154A7B69C2F88038DF +S31530002C60134BD3F880387B617B6923F0FF037B619C +S31530002C707B797A6913437B610D4A7B69C2F8803868 +S31530002C800B4BD3F880380A4A83F48073C2F8803805 +S31530002C90074BD3F8803803F40073BB60BA68FB681F +S31530002CA09A42F5D0024BD3F8A0383B613B69A7E195 +S31530002CB00040C840BB79002B3CD0A34BD3F8B0388A +S31530002CC003F40073FB60A04BD3F8B0389E4A23F46C +S31530002CD08033C2F8B0389C4BD3F8B0387B617B690F +S31530002CE023F0FF037B617B797A6913437B61964AD4 +S31530002CF07B69C2F8B038944A3B68C2F8C038924B08 +S31530002D00D3F8B038904A83F48073C2F8B0388E4B1B +S31530002D10D3F8B03803F40073BB60BA68FB689A42E4 +S31530002D20F5D0894BD3F8B038874A23F48033C2F8CC +S31530002D30B03864E1844BD3F8B03803F40073FB60E9 +S31530002D40814BD3F8B0387B617B6923F480337B6168 +S31530002D507B6943F480337B617B4A7B69C2F8B03848 +S31530002D60794BD3F8B0387B617B6923F0FF037B6105 +S31530002D707B797A6913437B61734A7B69C2F8B038D1 +S31530002D80714BD3F8B038704A83F48073C2F8B038D8 +S31530002D906D4BD3F8B03803F40073BB60BA68FB6888 +S31530002DA09A42F5D0684BD3F8D0383B613B6927E17E +S31530002DB0BB79002B34D0644BD3F8203903F400733D +S31530002DC0FB60614BD3F820395F4A23F48033C2F875 +S31530002DD020395D4BD3F820397B617B6923F0FF03C3 +S31530002DE07B617B797A6913437B61574A7B69C2F889 +S31530002DF02039554A3B68C2F83039534BD3F820391D +S31530002E00514A83F48073C2F820394F4BD3F82039B6 +S31530002E1003F40073BB60BA68FB689A42F5D0EEE003 +S31530002E20494BD3F8203903F40073FB60464BD3F893 +S31530002E3020397B617B6923F480337B617B6943F482 +S31530002E4080337B61404A7B69C2F820393E4BD3F8E8 +S31530002E5020397B617B6923F0FF037B617B797A695B +S31530002E6013437B61384A7B69C2F82039364BD3F835 +S31530002E702039354A83F48073C2F82039324BD3F87F +S31530002E80203903F40073BB60BA68FB689A42F5D008 +S31530002E902D4BD3F840393B613B69B1E0BB79002B10 +S31530002EA024D0294BD3F8E038274A23F48033C2F8AC +S31530002EB0E038254BD3F8E0387B617B6923F0FF039C +S31530002EC07B617B797A6913437B611F4A7B69C2F8E0 +S31530002ED0E0381D4A3B68C2F8F0381B4BD3F8003453 +S31530002EE0194A83F40043C2F8003488E0164BD3F80D +S31530002EF0E0387B617B6923F480337B617B6943F403 +S31530002F0080337B61104A7B69C2F8E0380E4BD3F8C8 +S31530002F10E0387B617B6923F0FF037B617B797A69DB +S31530002F2013437B61084A7B69C2F8E038064BD3F815 +S31530002F300034054A83F40043C2F80034024BD3F818 +S31530002F4010393B613B695BE00040C840BB79002BE0 +S31530002F5024D02D4BD3F8E0382B4A23F48033C2F8F3 +S31530002F60E038294BD3F8E0387B617B6923F0FF03E7 +S31530002F707B617B797A6913437B61234A7B69C2F82B +S31530002F80E038214A3B68C2F8F0381F4BD3F8703529 +S31530002F901D4A83F00103C2F8703530E01A4BD3F87E +S31530002FA0E0387B617B6923F480337B617B6943F452 +S31530002FB080337B61144A7B69C2F8E038124BD3F810 +S31530002FC0E0387B617B6923F0FF037B617B797A692B +S31530002FD013437B610C4A7B69C2F8E0380A4BD3F85D +S31530002FE07035094A83F00103C2F87035064BD3F8C1 +S31530002FF000393B613B6903E00448FFF717FB0023C8 +S3153000300018461837BD4680BD0040C840806800303D +S3153000301080B483B000AF0346FB71074AFB79DB010E +S3153000302013441B681B0A03F0070318460C37BD46CA +S315300030305DF8047B704700BF0000CC4080B483B09D +S3153000304000AF0346FB71064AFB79DB0113441B686C +S31530003050DBB2013318460C37BD465DF8047B70474A +S315300030600000CC4090B5ADF5237D00AF0246FB1D88 +S315300030701A7007F108031A4A184611464FF41E73A0 +S315300030801A4603F0BCF9FB1D1B781846FFF7C0FF44 +S31530003090C7F88402FB1D1B7807F10802DB001A44CF +S315300030A0D7F8843213441B78184600F003FC0446E4 +S315300030B0FB1D1B781846FFF7C1FF0346B4FBF3F33D +S315300030C0C7F88032D7F88032002B02D10548FFF797 +S315300030D0ADFAD7F88032184607F52377BD4690BD4E +S315300030E0BC680030A068003080B584B000AF78602E +S315300030F07B685B68D02B03D87B685B68672B02D80C +S315300031004848FFF793FA484BD3F8003203F400539C +S31530003110002B34D0444BD3F80032DA437B685B68FB +S315300031201340DBB2002B2AD13F4BD3F80032DA43BF +S315300031307B681B78DB03134003F4C033002B1ED1AE +S31530003140394BD3F8003203F48043002B07D1364B8A +S31530003150D3F80032344A43F48043C2F80032324B5B +S31530003160D3F8003203F08043002B56D02E4BD3F8E1 +S3153000317000322D4A23F08043C2F800324DE0FFF78B +S31530003180F5FB294BD3F8003223F00053FB60FB6884 +S3153000319003F4C043002B0BD0FB6823F4C043FB6021 +S315300031A0FB6843F08043FB601F4AFB68C2F800327D +S315300031B0FA681E4B1340FB607B685B68DAB27B684B +S315300031C01B78DB0303F4C0331A43FB681A43184BEE +S315300031D01343FB60144AFB68C2F80032BFF34F8FCB +S315300031E000BFBFF36F8F00BF12491E2000F04AFCAC +S315300031F000BF0D4BD3F8003203F00053002BF8D04C +S31530003200FB6843F48043FB60FB6823F08043FB603C +S31530003210054AFB68C2F8003200E000BF1037BD46F1 +S3153000322080BD00BF346B00300040C840007FFEFFD9 +S315300032300020004000CA9A3B80B584B000AF786069 +S31530003240524BD3F8403203F40003002B2CD04F4BB3 +S31530003250D3F880227B685B689A4225D14B4BD3F8F2 +S3153000326090227B689B689A421ED1484BD3F84032F5 +S3153000327003F40053002B07D1444BD3F84032434A72 +S3153000328043F40053C2F84032404BD3F8403203F097 +S315300032908043002B76D03D4BD3F840323B4A23F067 +S315300032A08043C2F840326DE0FFF760FB374BD3F80E +S315300032B07032364A43F08033C2F87032334BD3F82B +S315300032C04032FB60FA68324B1340002B0BD0FA6861 +S315300032D0304B1340FB60FB6843F08043FB602B4A66 +S315300032E0FB68C2F84032294B6FF07042C3F8A02217 +S315300032F0264A7B685B68C2F88032244A7B689B68C2 +S31530003300C2F89032244BFB60204AFB68C2F8403248 +S3153000331022491E2000F0B6FBFA68214B1343FB60AE +S315300033201A4AFB68C2F840321C49FA2000F0AAFB60 +S31530003330FB6823F40063FB60144AFB68C2F8403232 +S3153000334000BF124BD3F8403203F00053B3F1005FA5 +S31530003350F7D1FB6843F40053FB600C4AFB68C2F8B4 +S315300033604032FB6823F08043FB60084AFB68C2F8B2 +S315300033704032064BD3F87032044A03F07F33C2F83A +S31530003380703200E000BF1037BD4680BD0040C840F7 +S3153000339000208000FFDF7FFF0800004000CA9A3B14 +S315300033A00008800080B586B000AF0346FB710B463F +S315300033B0BB7113467B7100237B6100233B617B79B4 +S315300033C0232B02D87B790B2B02D83348FFF72EF903 +S315300033D097F90730022B02D0032B05D009E02F4B8B +S315300033E07B612F4B3B6107E02E4B7B612E4B3B6164 +S315300033F002E02E48FFF71AF97B691A68BB79DB00C1 +S31530003400402101FA03F31340FB607B691A68BB79EC +S31530003410DB00802101FA03F31A437B691A607B696A +S315300034201A68BB79DB003F2101FA03F3DB431A400C +S315300034307B691A607B691A687B7903F03F01BB7937 +S31530003440DB0001FA03F31A437B691A603B691A6899 +S31530003450BB79022101FA03F35A403B691A607B6952 +S315300034601A68BB79DB00802101FA03F3DB431A408B +S315300034707B691A6000BF7B691A68BB79DB00402123 +S3153000348001FA03F31A40FB689A42F4D000BF00BF3A +S315300034901837BD4680BD00BFA46B00307042C840AF +S315300034A05042C8403042C8402042C840E46B0030E9 +S315300034B080B584B000AF03460A46FB711346BB7134 +S315300034C00023FB600023BB6097F90730022B06D040 +S315300034D097F90730032B02D02348FFF7A7F897F95F +S315300034E00730022B02D0032B0DD019E01F4BD3F837 +S315300034F07022BB79DB003F2101FA03F31340BB6036 +S315300035001B4BFB600FE0194BD3F83022BB79DB0045 +S315300035103F2101FA03F31340BB60164BFB6002E018 +S315300035201548FFF783F8BB79DB00BA6822FA03F354 +S31530003530BB60BB680B2B02D9BB68232B02D90F4863 +S31530003540FFF774F8BB68002B08D0FA68BB68B2FB8B +S31530003550F3F21346DB0013445B0000E00023184609 +S315300035601037BD4680BD00BF006C00300040C840FB +S3153000357000A4781F00389C1C486C0030646C003006 +S3153000358080B584B000AF78603E4BD3F8103203F488 +S315300035900013002B29D03B4BD3F81032DA437B682B +S315300035A01B68134003F00703002B1ED1354BD3F8AD +S315300035B0103203F40053002B07D1324BD3F81032BC +S315300035C0304A43F40053C2F810322E4BD3F810323F +S315300035D003F08043002B50D02A4BD3F81032294ABF +S315300035E023F08043C2F8103247E0FFF7BFF9254B8E +S315300035F0D3F83032234A43F08033C2F830327B6816 +S315300036001B6803F00702204B1343FB601D4AFB681F +S31530003610C2F810321D491E2000F034FAFA681C4BED +S315300036201343FB60174AFB68C2F8103217491E2055 +S3153000363000F028FAFB6823F40063FB60114AFB684C +S31530003640C2F8103200BF0F4BD3F8103203F00053DC +S31530003650B3F1005FF7D1FA6842F208031343FB6017 +S31530003660084AFB68C2F81032FB6823F08043FB60DF +S31530003670044AFB68C2F8103200E000BF1037BD467E +S3153000368080BD00BF0040C8401000004000CA9A3BD1 +S315300036900008200080B584B000AF786039607B6860 +S315300036A0002B02D13348FEF7C1FF4FF41C63FB6099 +S315300036B03A68FB689A4202D83B689B2B02D82E4860 +S315300036C0FEF7B4FF3A68FB689A4206D37B6803225A +S315300036D01A707B68D0225A6046E0FB685B083A680D +S315300036E09A420AD37B6803221A703B68234AA2FBAC +S315300036F00323DA087B685A6036E0FB689B083A6831 +S315300037009A420AD37B6800221A703B681B4AA2FB96 +S3153000371003239A087B685A6026E0FB68DB083A6820 +S315300037209A420AD37B6801221A703B68134AA2FB7D +S3153000373003235A087B685A6016E0FB681B093A680F +S315300037409A420BD97B6802221A703B685B000B4A9F +S31530003750A2FB03235A087B685A6005E07B68022285 +S315300037601A707B6868225A60002318461037BD46A7 +S3153000377080BD00BFA86C0030C06C0030ABAAAAAACE +S3153000378080B400AF0A4B1B6A03F01003002B0AD13A +S31530003790074B14221A6200BF054B1B6A03F08043A5 +S315300037A0B3F1804FF8D100BFBD465DF8047B70475A +S315300037B00040C84080B588B000AF0346FB71002397 +S315300037C0FB6197F90730052B59D801A252F823F03F +S315300037D0E9370030253800302B3800306538003076 +S315300037E06B380030753800302C4BD3F80032DBB2F2 +S315300037F0FB602A4BD3F80032DB0B03F00303BB60CC +S31530003800BB680133012202FA03F3BB60BB685B007D +S31530003810234AB2FBF3F3FB61FB69FA6802FB03F35D +S31530003820FB612FE01F4BFB612CE01C4BD3F8903231 +S3153000383003F07F03BB61194BD3F8803223F040434A +S315300038407B61164BD3F8A03223F040433B617A6953 +S315300038503B69B2FBF3F2BB691344114A02FB03F333 +S31530003860FB610FE0104BFB610CE00420FEF768FEB5 +S31530003870F86107E00520FEF763FEF86102E00B48C9 +S31530003880FEF7D4FEFB69002B02D10948FEF7CEFEC7 +S31530003890FB6918462037BD4680BD00BF0040C84092 +S315300038A000366E0100CA9A3B00389C1C486D0030C9 +S315300038B0646D003080B584B000AF0346FB710023E1 +S315300038C0FB60FB791E2B00F2BD8001A252F823F07B +S315300038D04D390030673900306D390030733900307A +S315300038E079390030793900307F3900307F3900300E +S315300038F08939003089390030933900309F390030AA +S31530003900AB390030B7390030C3390030C3390030F5 +S31530003910453A0030CD390030D9390030E5390030FC +S31530003920F1390030FD390030FD390030073A0030CA +S31530003930153A0030293A0030293A0030333A00300F +S31530003940333A00303D3A00303D3A0030444BD3F8FC +S31530003950C03003F00403002B02D0424BFB6074E00E +S31530003960414BFB6071E0414BFB606EE0404BFB60CE +S315300039706BE0404BFB6068E03D4BFB6065E0002050 +S31530003980FFF718FFF86060E00220FFF713FFF860DA +S315300039905BE000210220FFF78BFDF86055E0012146 +S315300039A00220FFF785FDF8604FE002210220FFF785 +S315300039B07FFDF86049E003210220FFF779FDF860CA +S315300039C043E00320FFF7F6FEF8603EE000210320D7 +S315300039D0FFF76EFDF86038E001210320FFF768FD40 +S315300039E0F86032E002210320FFF762FDF8602CE038 +S315300039F003210320FFF75CFDF86026E00120FFF786 +S31530003A00D9FEF86021E00120FFF7D4FE03465B08BB +S31530003A10FB601AE00120FFF7CDFE0346164AA2FBF3 +S31530003A2003239B08FB6010E00420FFF7C3FEF86019 +S31530003A300BE00520FFF7BEFEF86006E002F0A3FAC1 +S31530003A40F86002E00D48FEF7F1FDFB68002B02D16D +S31530003A500B48FEF7EBFDFB6818461037BD4680BDB8 +S31530003A600040C8400024F40000093D00006CDC0230 +S31530003A7000366E010084D717CDCCCCCC7C6D0030AF +S31530003A80986D0030F0B585B000AF786039607B68EE +S31530003A90002B02D11E48FEF7C9FD7B6818464FF051 +S31530003AA000013B681A464FF0000302FB01FC00FBA5 +S31530003AB003F66644A0FB020173181946154A4FF007 +S31530003AC0000302F0D0FD02460B46C7E90223D7E9D0 +S31530003AD00223012B08BF002A02D30F48FEF7A6FDAA +S31530003AE0D7E902014FF003024FF0000302F0BBFDAD +S31530003AF002460B46941843EB0305C7E90245BB68FB +S31530003B00184602F061FA00BF1437BD46F0BD00BF5B +S31530003B10B46D003040420F00D86D003080B483B0B1 +S31530003B2000AF03460A46FB711346BB71BB79194693 +S31530003B30114AFB7903F540735B0113441B684B4014 +S31530003B4003F00103002B10D0BB791A460A49FB79E2 +S31530003B5002F0010203F540735B010B441A60BFF3B8 +S31530003B604F8F00BFBFF36F8F00BF00BF0C37BD460E +S31530003B705DF8047B704700BF0000CC4080B584B050 +S31530003B8000AF78600023FB6009E00D4AFB6852F80D +S31530003B9023307A689A4206D0FB680133FB60FB68B3 +S31530003BA00F2BF2D900E000BFFB680F2B02D9054876 +S31530003BB0FEF73CFDFB6818461037BD4680BD00BF9A +S31530003BC0006E0030506E003080B586B000AFF860C1 +S31530003BD0B9607A60F868FFF7D1FF78617B690D2BA1 +S31530003BE00CD8204A7B6913441B788A2B06D01D4A91 +S31530003BF07B6913441B78184602F0F6F9FB685A695C +S31530003C000121BB6801FA03F3DB431A40FB685A61B2 +S31530003C107B681B78002B0AD1FB685A680121BB6888 +S31530003C2001FA03F3DB431A40FB685A600FE07B6806 +S31530003C305B781A46B968F86800F016F8FB685A6877 +S31530003C400121BB6801FA03F31A43FB685A607B68AB +S31530003C509B781A46B968F86802F0D4F900BF18376D +S31530003C60BD4680BD406E003080B584B000AFF86090 +S31530003C70B9601346FB71BB681F2B02D90E48FEF79D +S31530003C80D5FCFB79002B0AD1FB681A680121BB6889 +S31530003C9001FA03F3DB431A40FB681A6008E0FB685D +S31530003CA01A680121BB6801FA03F31A43FB681A60EC +S31530003CB000BF1037BD4680BD846E003080B487B0FB +S31530003CC000AFF860B9601346FB71BB683B61FB68B7 +S31530003CD0DA690121BB6801FA03F3DB431A40FB685A +S31530003CE0DA61BB680F2B03D8FB680C337B6105E0C8 +S31530003CF0FB6810337B613B69103B3B61FB79013BD1 +S31530003D00042B52D801A252F823F000BF213D0030D7 +S31530003D10393D00305D3D0030813D0030973D00300B +S31530003D207B691A683B695B00032101FA03F3DB43C5 +S31530003D301A407B691A6039E07B691A683B695B0017 +S31530003D40032101FA03F3DB431A403B695B0001218F +S31530003D5001FA03F31A437B691A6027E07B691A6814 +S31530003D603B695B00032101FA03F3DB431A403B69ED +S31530003D705B00022101FA03F31A437B691A6015E0EE +S31530003D807B691A683B695B00032101FA03F31A4326 +S31530003D907B691A600AE0FB68DA690121BB6801FABF +S31530003DA003F31A43FB68DA6100E000BF00BF1C373B +S31530003DB0BD465DF8047B704780B483B000AF0346E0 +S31530003DC00A46FB711346BB71BB791946114AFB791A +S31530003DD003F540735B0113441B684B4003F001034A +S31530003DE0002B10D0BB791A460A49FB7902F0010242 +S31530003DF003F540735B010B441A60BFF34F8F00BF6E +S31530003E00BFF36F8F00BF00BF0C37BD465DF8047B34 +S31530003E10704700BF0000CC4080B584B000AF7860FA +S31530003E200023FB6009E00D4AFB6852F823307A68BC +S31530003E309A4206D0FB680133FB60FB680C2BF2D943 +S31530003E4000E000BFFB680C2B02D90548FEF7EEFBFD +S31530003E50FB6818461037BD4680BD00BFA06E0030E7 +S31530003E60E46E003080B58CB000AFF860B9607A602F +S31530003E70BB68002B02D1A248FEF7D8FBBB681B6893 +S31530003E80002B02D19F48FEF7D1FBBB681B7A042B6F +S31530003E9002D99D48FEF7CAFBBB685B7A042B02D970 +S31530003EA09A48FEF7C3FB0023FB62BB681B68FB61C5 +S31530003EB0002387F823300023FB84042387F822303D +S31530003EC041E07A6813469B0013445B001946BB6891 +S31530003ED01B6897F8222002FB03F3B1FBF3F305339B +S31530003EE08B4AA2FB0323DB08BB84BB8C002B01D19E +S31530003EF00123BB8497F82230BA8C02FB03F37A682D +S31530003F00B2FBF3F37B61BB681B687A699A4204D9CA +S31530003F10BB681B687A69D31A03E0BB681A687B6989 +S31530003F20D31A3B613A69FB699A4207D83B69FB6110 +S31530003F3097F8223087F82330BB8CFB8497F82230F1 +S31530003F40013387F8223097F82230202BB9D9BB6855 +S31530003F501B68704AA2FB03235A0913465B001344BD +S31530003F60FA699A4203D940F22153FB62FCE0F868C1 +S31530003F70FFF752FFB861684ABB6913441B7818468D +S31530003F8002F051F8F86802F05CF8FB681B69BB6216 +S31530003F9097F82330032B07D997F82330072B03D80C +S31530003FA0BB6A43F40033BB62BB6A23F0F853BB628F +S31530003FB097F82330013B1B0603F0F853BA6A1343D4 +S31530003FC0BB62BA6A554B1340BB62FB8CC3F30C021F +S31530003FD0BB6A1A43FB681A61FB681B6923F00052FF +S31530003FE0FB681A61FB689A694D4B1340BB62BB682C +S31530003FF01B791A46BB68DB7B1B0203F4E0631A436A +S31530004000BB689B7B9B0003F004031343BA6A1343DC +S31530004010BB62BB685B79012B0DD1BB681B79002B6A +S3153000402004D0BB6A23F40063BB620CE0BB6A43F482 +S315300040300063BB6207E0BB681B79002B03D0BB6A09 +S3153000404043F01003BB62FB68BA6A9A61FB681B696E +S3153000405023F40053BB62BB68DB795B0303F4005285 +S31530004060BB6A1A43FB681A61BB685B7A1B04BA6881 +S31530004070127A1A43FB68DA62FB689B6A43F088025D +S31530004080FB689A62FB689B6A43F44042FB689A621B +S31530004090244BBB62BB6A43F00043BB62BB6A43F44A +S315300040A04043BB62FB685A6ABB685B7B1B0103F00B +S315300040B01001BB681B7B5B0103F020030B431A43E3 +S315300040C0FB685A62BB689B7A002B05D0FB685B6A3B +S315300040D043F00802FB685A62BB68DB7A002B05D0D6 +S315300040E0FB685B6A43F00102FB685A62BB689B79E6 +S315300040F0002B19D0BB6A43F00053BB6218E000BFF7 +S31530004100206F0030446F0030746F0030D06F003055 +S31530004110CDCCCCCC1F85EB51D46E003000E0FFFF08 +S31530004120E8F8FFFF00001F40BB6A23F00053BB6274 +S31530004130FB685A69BB6A1A43FB685A61FB689B691C +S31530004140BB62BB681B7C002B03D0BB6A43F40023E5 +S31530004150BB62BB685B7C002B03D0BB6A43F4802315 +S31530004160BB62FB68BA6A9A61FB6A18463037BD464D +S3153000417080BD00BF80B582B000AF78607B68002B11 +S3153000418002D11D48FEF752FA14220021786802F057 +S3153000419038F97B684FF4E1321A607B6800221A7175 +S315300041A07B6800225A717B6800229A717B680022F4 +S315300041B0DA717B6800221A727B6800225A727B6839 +S315300041C000229A727B680022DA727B6800225A7368 +S315300041D07B6800221A737B6800229A737B68002200 +S315300041E0DA737B6800221A747B6800225A7400BF27 +S315300041F00837BD4680BD00BF2C70003080B586B014 +S3153000420000AFF860B9607A60BB68002B02D1144801 +S31530004210FEF70CFABB687B617B683B6111E000BF3F +S31530004220FB685B6903F40003002BF9D07B691B78CC +S315300042301A46FB68DA617B6901337B613B69013B76 +S315300042403B613B69002BEAD100BFFB685B6903F435 +S315300042508003002BF9D0002318461837BD4680BDA1 +S315300042605070003080B500AF054B1B68054A526868 +S31530004270114605489847BFF34F8F00BF00BF80BD3A +S315300042805C0000202800002000C0074080B500AF49 +S31530004290054B1B68054A9268114605489847BFF397 +S315300042A04F8F00BF00BF80BD5C000020280000207B +S315300042B00000084080B500AF054B1B68054AD26840 +S315300042C0114605489847BFF34F8F00BF00BF80BDEA +S315300042D05C000020280000200040084080B500AF78 +S315300042E0054B1B68054A1269114605489847BFF3C6 +S315300042F04F8F00BF00BF80BD5C000020280000202B +S315300043000080084080B500AF054B1B68054A5269EE +S31530004310114605489847BFF34F8F00BF00BF80BD99 +S315300043205C0000202800002000C0084080B500AFA7 +S31530004330054B1B68054A9269114605489847BFF3F5 +S315300043404F8F00BF00BF80BD5C00002028000020DA +S315300043500000094080B500AF054B1B68054AD2699D +S31530004360114605489847BFF34F8F00BF00BF80BD49 +S315300043705C000020280000200040094080B500AFD6 +S31530004380054B1B68054A126A114605489847BFF324 +S315300043904F8F00BF00BF80BD5C000020280000208A +S315300043A00080094080B500AF054B1B68054A526A4C +S315300043B0114605489847BFF34F8F00BF00BF80BDF9 +S315300043C05C0000202800002000C0094080B500AF06 +S315300043D0054B1B68054A926A114605489847BFF354 +S315300043E04F8F00BF00BF80BD5C000020280000203A +S315300043F000000A4080B500AF054B1B68054AD26AFB +S31530004400114605489847BFF34F8F00BF00BF80BDA8 +S315300044105C000020280000200040C24080B584B0F7 +S3153000442000AF78600B46FB70FB78002B22D17B689F +S31530004430D3F8103523F005027B68C3F810252D49D3 +S315300044404FF47A70FFF71EFB7B68D3F8103523F4F0 +S31530004450C1527B68C3F8102526494FF47A70FFF7AE +S3153000446011FB7B68D3F8103523F400227B68C3F840 +S3153000447010253BE07B68D3F8103523F001027B68CA +S31530004480C3F810251B494FF47A70FFF7FBFA7B68A7 +S31530004490D3F8103543F400227B68C3F8102515494C +S315300044A04FF47A70FFF7EEFA7B68D3F81035FB607D +S315300044B0FB6823F4C053FB60FB78DB0203F4C05285 +S315300044C0FB68134343F02003FB607B68FA68C3F84C +S315300044D0102508494FF47A70FFF7D4FA7B68D3F881 +S315300044E0103543F004027B68C3F8102500BF10373F +S315300044F0BD4680BD00CA9A3B80B582B000AF7860B9 +S315300045000B46FB70FB78002B2FD07B68D3F8303509 +S3153000451043F001027B68C3F8302527494FF47A709F +S31530004520FFF7B0FA7B68D3F8303543F400327B6856 +S31530004530C3F8302520494FF47A70FFF7A3FA7B6829 +S31530004540D3F8303543F480227B68C3F830251A49D6 +S315300045504FF47A70FFF796FA7B68D3F8303523F04C +S3153000456004027B68C3F8302521E07B68D3F8303508 +S3153000457043F005027B68C3F830250F494FF47A7053 +S31530004580FFF780FA7B68D3F8303523F480227B68D6 +S31530004590C3F8302508494FF47A70FFF773FA7B6811 +S315300045A0D3F8303523F400327B68C3F8302500BFAA +S315300045B00837BD4680BD00BF00CA9A3B80B582B081 +S315300045C000AF78607B68002B02D10A48FEF72EF8E0 +S315300045D003220021786801F014FF7B6801221A70EB +S315300045E07B6803225A707B680F229A7000BF0837A7 +S315300045F0BD4680BD7470003080B584B000AF786041 +S3153000460039603B68002B02D13448FEF70FF87B68DF +S31530004610D3F8503523F070427B68C3F850257B6859 +S31530004620D3F850253B681B781B071A437B68C3F8C1 +S3153000463050257B68D3F8503523F070627B68C3F819 +S3153000464050257B68D3F850253B681B781B0603F052 +S3153000465070631A437B68C3F850257B68D3F85035AE +S31530004660FB60FA681E4B1340FB603B685B789B002F +S3153000467003F01C023B689B785B0103F4F073134331 +S3153000468043F00203BB60BB68C3F30C03FA68134301 +S31530004690FB607B68FA68C3F850257B68D3F86035D1 +S315300046A023F4E0527B68C3F860257B68D3F8603525 +S315300046B043F480627B68C3F860257B68D3F8603545 +S315300046C043F080727B68C3F8602506496420FFF7A3 +S315300046D0D9F900BF1037BD4680BD00BF9470003099 +S315300046E000E0FFFF00CA9A3B80B584B000AF814B33 +S315300046F0D3F888307F4A43F47003C2F888307D4B54 +S315300047007D4A9A607D4B1B889BB203F00403002BD5 +S3153000471007D07A4B1B889BB2784A23F004039BB2AE +S315300047201380774B1B889BB203F00403002B07D012 +S31530004730734B1B889BB2724A23F004039BB21380DF +S31530004740704B1B6803F40053002B03D06D4B6E4A3D +S315300047505A6007E06B4B4CF220525A60694B4DF66B +S3153000476028125A60674B4FF6FF729A60654B1B688A +S3153000477023F0A003634A43F020031360634B1B68A6 +S3153000478003F40053002B03D0604B5F4A5A6007E0B6 +S315300047905E4B4CF220525A605C4B4DF628125A60F2 +S315300047A05A4B4FF6FF729A60584B1B6823F0A003A2 +S315300047B0564A43F020031360554B1B6803F0010340 +S315300047C0002B05D0524B1B68514A23F0010313606E +S315300047D0484B5B6903F40033B3F5003F22D0454BB9 +S315300047E05B6903F40033002B1BD1BFF34F8F00BF3F +S315300047F0BFF36F8F00BF3F4B0022C3F85022BFF389 +S315300048004F8F00BFBFF36F8F00BF3A4B5B69394A9A +S3153000481043F400335361BFF34F8F00BFBFF36F8F45 +S3153000482000E000BF334B5B6903F48033B3F5803F60 +S315300048303FD0304B5B6903F48033002B38D12D4B9E +S315300048400022C3F88420BFF34F8F00BF294BD3F823 +S315300048508030FB60FB685B0BC3F30E03BB60FB6809 +S31530004860DB08C3F309037B60BB685A0143F6E07388 +S3153000487013407A6892071F491343C1F860327B6848 +S315300048805A1E7A60002BEFD1BB685A1EBA60002BD5 +S31530004890E5D1BFF34F8F00BF164B5B69154A43F422 +S315300048A080335361BFF34F8F00BFBFF36F8F00E08C +S315300048B000BF184B1B6F174A23F400531367164B70 +S315300048C0154A1B69D363144B4FF0FF321A61094BFB +S315300048D05B69084A43F010035361BFF34F8F00BF43 +S315300048E0BFF36F8F00BF01F0C9FB00BF1037BD4665 +S315300048F080BD00BF00ED00E0002000300000034026 +S31530004900004003400080034020C528D90000C14044 +S3153000491010E000E000400E400040C04080B58AB054 +S3153000492000AF786039600023FB847B68002B02D1AE +S315300049302848FDF77BFE3B68002B02D12648FDF761 +S3153000494075FE3B681B7B0C2B02D92448FDF76EFEA7 +S315300049503B681B7B1A46224B53F82230002B02D180 +S315300049602048FDF763FE07F108031846FFF702FCFF +S315300049703B689B7A002B14BF01230023DBB27B7686 +S315300049803B68DB7A002B14BF01230023DBB23B7676 +S315300049903B681B7B1A46124B53F822003B681A6859 +S315300049A007F108031946FFF75DFA38623B6A002BB8 +S315300049B005D0386A01F069FB0346FB8405E07B6865 +S315300049C0FB613B681A7BFB691A70FB8C18462837EB +S315300049D0BD4680BDE87000300C710030307100305B +S315300049E0B47000309871003080B586B000AFF86092 +S315300049F0B9607A60FB68002B02D11048FDF716FECD +S31530004A00BB68002B02D10E48FDF710FE7B68002BE9 +S31530004A1002D10C48FDF70AFEFB687B617B691B7887 +S31530004A201A46094B53F822307A68B9681846FFF7A8 +S31530004A30E5FB002318461837BD4680BDDC710030D3 +S31530004A400072003024720030B470003080B483B00D +S31530004A5000AF0346FB71074AFB79DB0113441B6841 +S31530004A601B0A03F0070318460C37BD465DF8047B76 +S31530004A70704700BF0000CC4080B483B000AF03461F +S31530004A80FB71064AFB79DB0113441B68DBB2013349 +S31530004A9018460C37BD465DF8047B70470000CC40A5 +S31530004AA090B5ADF5237D00AF0246FB1D1A7007F1B8 +S31530004AB008031A4A184611464FF41E731A4601F077 +S31530004AC09EFCFB1D1B781846FFF7C0FFC7F8840213 +S31530004AD0FB1D1B7807F10802DB001A44D7F8843235 +S31530004AE013441B781846FEF7E5FE0446FB1D1B787B +S31530004AF01846FFF7C1FF0346B4FBF3F3C7F880321D +S31530004B00D7F88032002B02D10548FDF78FFDD7F854 +S31530004B108032184607F52377BD4690BDE0720030E7 +S31530004B204872003080B483B000AF0346FB71074A49 +S31530004B30FB79DB0113441B681B0A03F00703184695 +S31530004B400C37BD465DF8047B704700BF0000CC4093 +S31530004B5080B483B000AF0346FB71064AFB79DB01B4 +S31530004B6013441B68DBB2013318460C37BD465DF87B +S31530004B70047B70470000CC4080B582B000AF03465E +S31530004B803960FB713B68002B02D11A48FDF74EFDA8 +S31530004B903B685B781B0503F470023B689B781B040B +S31530004BA003F470231A433B68DB781B0203F4E0639B +S31530004BB01A433B681B79013BDBB21A433B681B78CF +S31530004BC0002B05D03B681B781B0603F0807300E092 +S31530004BD000230948F9791A43CB0103441A60BFF31D +S31530004BE04F8F00BFBFF36F8F00BF00BF0837BD4682 +S31530004BF080BD00BF587500300000CC4090B5ADF593 +S31530004C00237D00AF0246FB1D1A7007F108031A4ACE +S31530004C10184611464FF41E731A4601F0F0FBFB1D81 +S31530004C201B781846FFF77EFFC7F88402FB1D1B78FA +S31530004C3007F10802DB001A44D7F8843213441B7894 +S31530004C401846FEF737FE0446FB1D1B781846FFF75D +S31530004C507FFF0346B4FBF3F3C7F88032D7F88032D0 +S31530004C60002B02D10548FDF7E1FCD7F88032184613 +S31530004C7007F52377BD4690BDF07500304872003099 +S31530004C8080B58AB000AF07F1200300221A601A718E +S31530004C901621724801F058FB07F11C031846FFF73E +S31530004CA08DFC07F11C0319466D48FFF7A5FC012161 +S31530004CB06B48FFF7B3FB01216948FFF71DFC07F18D +S31530004CC0080300221A605A609A60DA601A6101237A +S31530004CD03B7216233B6103237B60FEF751FD0023B5 +S31530004CE087F82330012387F8243007F1200319464B +S31530004CF00020FFF741FF07F1200319460820FFF790 +S31530004D003BFF4FF47A7001F046F9042387F82330DD +S31530004D10012387F8243007F1200319460020FFF7D6 +S31530004D202BFF002387F82330F02387F8243007F150 +S31530004D30200319460820FFF71FFF07F1080318461E +S31530004D40FEF77AFA3B1D1846FEF71AFC062387F85B +S31530004D502330162387F8243007F12003194619200B +S31530004D60FFF70AFF102201210220FEF71BFB062364 +S31530004D7087F82330032387F8243007F120031946B8 +S31530004D800420FFF7F9FE042387F82330022387F83F +S31530004D90243007F1200319460220FFF7EDFE0023E9 +S31530004DA087F82330012387F8243007F1200319468A +S31530004DB02520FFF7E1FE002387F82330012387F80B +S31530004DC0243007F1200319462920FFF7D5FE0023AA +S31530004DD087F82330012387F8243007F1200319465A +S31530004DE00E20FFF7C9FE002387F82330012387F80A +S31530004DF0243007F1200319460F20FFF7BDFE0023AC +S31530004E0087F82330012387F8243007F12003194629 +S31530004E102B20FFF7B1FE002387F82330012387F8D4 +S31530004E20243007F1200319460D20FFF7A5FE002395 +S31530004E3087F82330012387F8243007F120031946F9 +S31530004E403120FFF799FE0020FFF7D8FE0346054ACA +S31530004E50136000BF2837BD4680BD00BF0080CA4002 +S31530004E600040C8400000002080B483B000AF034645 +S31530004E700A46FB711346BB71BB791946114AFB7959 +S31530004E8003F540735B0113441B684B4003F0010389 +S31530004E90002B10D0BB791A460A49FB7902F0010281 +S31530004EA003F540735B010B441A60BFF34F8F00BFAD +S31530004EB0BFF36F8F00BF00BF0C37BD465DF8047B74 +S31530004EC0704700BF0000CC4080B584B002AF3120BF +S31530004ED001F063FA01233B7100237B710023BB7120 +S31530004EE03B1D1A4603210848FEF76EFE0023019348 +S31530004EF0064B0093002300220A21054801F05BFA95 +S31530004F0000BF0837BD4680BD0040C64060830E40B6 +S31530004F101C810E4010B5174A0020174901F0F0FAEF +S31530004F204FF480734FF480620021124801F083FA07 +S31530004F30124A1349012001F0E3FA4FF480734FF41B +S31530004F40806200210D4801F076FA0E4B1B68022B69 +S31530004F500EDD0D4A02200A4901F0D2FA4FF4807371 +S31530004F604FF4806200210848BDE8104001F063BA72 +S31530004F7010BD00BF640000206C780030A000002017 +S31530004F806E78003068780030DC0000202DE9F04182 +S31530004F90D56880460E461446EB02576905D545F06E +S31530004FA01005384601F050FCA061124B2B408BB1F6 +S31530004FB0A169384601F051FC002806DAE3684FF063 +S31530004FC0FF3043F08003E36011E025F4003525F02F +S31530004FD01005E56032464146384601F044FC20F083 +S31530004FE00041761AA1690E44A6610028E6D1BDE8D3 +S31530004FF0F08100BF10000200F7B5044698B103B047 +S31530005000BDE8F04001F00FBB07FB0460013401F04E +S315300050100AFB002818BF4FF0FF35019BA342F3DC93 +S31530005020284603B0F0BD034B05463C27024E1B68AD +S315300050300193F2E768780030640000202DE9F843E8 +S31530005040C66804463C4B0D46904633401BB9022792 +S315300050503846BDE8F883D0F81490484601F0F7FB9F +S3153000506007460028F3D1B8F1010F2CD0B8F1020F62 +S315300050702ED0B8F1000FEAD1002DE8DBB00404D50C +S315300050802368E26A9A4238BFE362A169A9420EDC1C +S315300050902368E26A934238BF134622695818801A49 +S315300050A0A84204DB206B01EB000CAC4534DC00235A +S315300050B046F02006A562C4E9013326F4032626F01D +S315300050C04006E660C4E7204600F038F80544D3E7EA +S315300050D0484601F0B9FBB0F1000CE26804DA42F060 +S315300050E080020127E260B3E723681846A3691946B0 +S315300050F0E36A98422CBF0918C91892060B462169F3 +S31530005100A3EB010303D5A26A9342B8BF13466345A6 +S31530005110ACBFED186544AFE76D1AB10744BF281A26 +S31530005120A060F10726F0200644BF9B1AEB1A1544FF +S3153000513048BF63602560C0E703001000C3689A0764 +S3153000514005D10E4B21224FF0FF301A60704703F025 +S3153000515020021B0309D512B1806A013870470368F3 +S31530005160826900691344181AF7E70AB1806A7047F2 +S315300051700368826900691344181A7047600000207A +S3153000518080B586B000AFF860B9603B601346FB71FE +S3153000519000237B6100237B611CE0BB681B68013305 +S315300051A07F2B08D9BB681B681946F868FDF74AFAA1 +S315300051B0BB6800221A60BB681B681A46FB6813443A +S315300051C0FA791A70BB681B685A1CBB681A607B690F +S315300051D001337B617A693B689A42DEDB00BF00BFF0 +S315300051E01837BD4680BD80B485B000AF7860396071 +S315300051F00023FB600023FB7220E07B681B685A1C8F +S315300052007B681A607B681B681B78BB72BB7A2F2B56 +S315300052100DD9BB7A392B0AD8FA6813469B0013444A +S315300052205B001A46BB7A1344303BFB6006E07B6872 +S315300052301B685A1E7B681A600123FB72FB7A002BAF +S31530005240DBD0FB6818461437BD465DF8047B7047E3 +S3153000525080B489B000AFF860B9607A60FB681B68CB +S31530005260FB610623BB610023FB75FB690133FB61E0 +S31530005270FB691B782E2B24D10023BB610023FB75E1 +S315300052801BE0FB690133FB61FB691B78BB75BB7D9A +S315300052902F2B0DD9BB7D392B0AD8BA6913469B0003 +S315300052A013445B001A46BB7D1344303BBB6104E0BC +S315300052B0FB69013BFB610123FB75FB7D002BE0D0D5 +S315300052C002E0FB69013BFB61FB68FA691A60BB6966 +S315300052D018462437BD465DF8047B704780B485B0E8 +S315300052E000AF0346FB710023FB60FB796F2B08D0C0 +S315300052F0FB79622B05D0FB79702B02D0FB79752BAD +S3153000530001D10123FB60FB6818461437BD465DF8B2 +S31530005310047B704780B485B000AF0346FB71002331 +S31530005320FB60FB79642B02D0FB79692B01D1012319 +S31530005330FB60FB6818461437BD465DF8047B704742 +S3153000534090B585B000AFF860B9607A60FB70BA6826 +S315300053507B68D31A7C6A2022F96AB86AA04708E0CB +S315300053603B6A5A1E3A621A787C6A0123F96AB86A2D +S31530005370A0473B6A1B78002BF2D100BF00BF143721 +S31530005380BD4690BD90B585B000AFF860B9607A6023 +S31530005390FB70BA687B68D31A7C6A2022F96AB86ACD +S315300053A0A04708E03B6A5A1E3A621A787C6A0123A3 +S315300053B0F96AB86AA0473B6A1B78002BF2D100BF66 +S315300053C000BF1437BD4690BD80B485B000AF0346EC +S315300053D0FB710023FB60FB79662B02D0FB79462BF1 +S315300053E001D10123FB60FB6818461437BD465DF8D2 +S315300053F0047B704780B485B000AF0346FB71002351 +S31530005400FB60FB79782B02D0FB79582B01D1012335 +S31530005410FB60FB6818461437BD465DF8047B704761 +S3153000542080B485B000AF78607B681B68FB60FB6832 +S315300054300133FB60FB681B78FB72FB7A682BF6D076 +S31530005440FB7A6C2BF3D0FB68013BFB607B68FA6818 +S315300054501A6000BF1437BD465DF8047B704780B4D0 +S3153000546085B000AF0346FB71FB796F2B02D1082361 +S31530005470FB730DE0FB79622B02D10223FB7307E04D +S31530005480FB79702B02D11023FB7301E00A23FB73E7 +S31530005490FB7B18461437BD465DF8047B704780B4F5 +S315300054A08DB000AFF860B9607A603B600023FB6175 +S315300054B0FB68BB61BB695A1CBA6100221A707B68F3 +S315300054C0002B33D0BB681B68FB62FB6A002B29D1EB +S315300054D0BB6930221A70FB690133FB61FB6960E0FE +S315300054E0FA6A3B6892FBF3F33B613B693A6802FB2D +S315300054F003F3FA6AD31ABB62BB6A002B04DABB6ABF +S31530005500C3F13003BB6202E0BB6A3033BB623B6936 +S31530005510FB62BB695A1CBA61BA6AD2B21A70FB69AD +S315300055200133FB61FB6A002BDAD139E0BB681B68BB +S315300055307B627B6A002B30D1BB6930221A70FB69E3 +S315300055400133FB61FB692CE03B687A6AB2FBF3F30B +S315300055507B613B687A6902FB03F37A6AD31A3B6252 +S315300055603B6A092B03D83B6A30333B620AE097F833 +S315300055703830002B01D0412200E061223B6A1344CF +S315300055800A3B3B627B697B62BB695A1CBA613A6AE9 +S31530005590D2B21A70FB690133FB617B6A002BD3D11F +S315300055A0FB6918463437BD465DF8047B704790B5C5 +S315300055B09FB004AFF860B9607A603B600023FB654A +S315300055C00023BB650023FB61002387F85730002397 +S315300055D07B61FB687B647B6C1B78002B00F0298138 +S315300055E07B6C1B7887F8563097F85630252B0BD0C6 +S315300055F097F8562007F11C013C6801237868A047CC +S315300056007B6C01337B6413E1012387F8633007F148 +S31530005610080207F1440311461846FFF7E4FD3865E2 +S3153000562007F1080107F1440300221846FFF710FE80 +S31530005630F86407F144031846FFF7F2FE7B6C01333A +S315300056407B647B6C1B7887F8563097F85630184653 +S31530005650FFF760FE0346012B25D1BB681A1DBA60E1 +S315300056601B68BB6107F1180107F1200097F863301A +S3153000567000930A230122FFF712FFB86507F12002D3 +S31530005680BB6D1344FB65BA6D07F11C0303937B684E +S3153000569002933B680193FB6D00930023396D002024 +S315300056A0FFF74EFEC1E097F856301846FFF78CFEEE +S315300056B00346012B06D1BB68073323F007030833B3 +S315300056C0BB60B2E097F856301846FFF793FE0346B4 +S315300056D0012B2ED197F85630782B02D1002387F83C +S315300056E06330BB681A1DBA601B687B6107F1140111 +S315300056F007F1200097F86330009310230022FFF75C +S31530005700CEFEB86507F12002BB6D1344FB65BA6D5A +S3153000571097F8631007F11C0303937B6802933B6889 +S315300057200193FB6D00930B46396D0020FFF72AFE7F +S315300057307BE097F856301846FFF7D0FD0346012B2D +S315300057402FD1BB681A1DBA601B687B6197F856303B +S315300057501846FFF784FE034687F8573097F85720E8 +S3153000576007F1140107F1200097F8633000931346D0 +S315300057700022FFF794FEB86507F12002BB6D134493 +S31530005780FB65BA6D07F11C0303937B6802933B6894 +S315300057900193FB6D00930023396D0020FFF7D0FD98 +S315300057A043E097F85630632B0DD1BB681A1DBA60AB +S315300057B01B68BB64BB6CDAB207F11C013C68012381 +S315300057C07868A04731E097F85630732B25D1BB68FF +S315300057D01A1DBA601B687B667B6E002B25D0786EEF +S315300057E000F048FE0346BB653A6DBB6DD31A07F130 +S315300057F01C013C6820227868A04709E07B6E5A1C61 +S315300058007A661A7807F11C013C6801237868A0474C +S315300058107B6E1B78002BF1D107E097F8562007F105 +S315300058201C013C6801237868A0477B6C01337B649C +S31530005830D1E600BFFB6918466C37BD4690BD08B54A +S31530005840FCF77DFE08BD08B5FCF779FE08BD08B546 +S31530005850FCF775FE08BD08B5FCF771FE08BD08B546 +S31530005860FCF76DFE08BD08B5FCF769FE08BD08B546 +S31530005870FCF765FE08BD08B5FCF761FE08BD08B546 +S31530005880FCF75DFE08BD08B5FCF759FE08BD08B546 +S31530005890FCF755FE08BD08B5FCF751FE08BD08B546 +S315300058A0FCF74DFE08BD08B5FCF749FE08BD08B546 +S315300058B0FCF745FE08BD08B5FCF741FE08BD08B546 +S315300058C0FCF73DFE08BD08B5FCF739FE08BD08B546 +S315300058D0FCF735FE08BD08B5FCF731FE08BD08B546 +S315300058E0FEF7C0FC08BD08B5FEF7D0FC08BD08B50C +S315300058F0FEF7E0FC08BD08B5FEF7F0FC08BD08B5BC +S31530005900FEF700FD08BD08B5FEF710FD08BD08B569 +S31530005910FEF720FD08BD08B5FEF730FD08BD08B519 +S31530005920FEF740FD08BD08B5FEF750FD08BD08B5C9 +S31530005930FEF760FD08BD08B5FCF701FE08BD08B5E9 +S31530005940FCF7FDFD08BD08B5FCF7F9FD08BD08B547 +S31530005950FCF7F5FD08BD08B5FCF7F1FD08BD08B547 +S31530005960FCF7EDFD08BD08B5FCF7E9FD08BD08B547 +S31530005970FCF7E5FD08BD08B5FCF7E1FD08BD08B547 +S31530005980FCF7DDFD08BD08B5FCF7D9FD08BD08B547 +S31530005990FCF7D5FD08BD08B5FCF7D1FD08BD08B547 +S315300059A0FCF7CDFD08BD08B5FCF7C9FD08BD08B547 +S315300059B0FCF7C5FD08BD08B5FCF7C1FD08BD08B547 +S315300059C0FCF7BDFD08BD08B5FCF7B9FD08BD08B547 +S315300059D0FCF7B5FD08BD08B5FCF7B1FD08BD08B547 +S315300059E0FCF7ADFD08BD08B5FCF7A9FD08BD08B547 +S315300059F0FCF7A5FD08BD08B5FCF7A1FD08BD08B547 +S31530005A00FCF79DFD08BD08B5FCF799FD08BD08B546 +S31530005A10FCF795FD08BD08B5FCF791FD08BD08B546 +S31530005A20FCF78DFD08BD08B5FCF789FD08BD08B546 +S31530005A30FCF785FD08BD08B5FCF781FD08BD08B546 +S31530005A40FCF77DFD08BD08B5FCF779FD08BD08B546 +S31530005A50FCF775FD08BD08B5FCF771FD08BD08B546 +S31530005A60FCF76DFD08BD08B5FCF769FD08BD08B546 +S31530005A70FCF765FD08BD08B5FCF761FD08BD08B546 +S31530005A80FCF75DFD08BD08B5FCF759FD08BD08B546 +S31530005A90FCF755FD08BD08B5FCF751FD08BD08B546 +S31530005AA0FCF74DFD08BD08B5FCF749FD08BD08B546 +S31530005AB0FCF745FD08BD08B5FCF741FD08BD08B546 +S31530005AC0FCF73DFD08BD08B5FCF739FD08BD08B546 +S31530005AD0FCF735FD08BD08B5FCF731FD08BD08B546 +S31530005AE0FCF72DFD08BD08B5FCF729FD08BD08B546 +S31530005AF0FCF725FD08BD08B5FCF721FD08BD08B546 +S31530005B00FCF71DFD08BD08B5FCF719FD08BD08B545 +S31530005B10FCF715FD08BD08B5FCF711FD08BD08B545 +S31530005B20FCF70DFD08BD08B5FCF709FD08BD08B545 +S31530005B30FCF705FD08BD08B5FCF701FD08BD08B545 +S31530005B40FCF7FDFC08BD08B5FCF7F9FC08BD08B547 +S31530005B50FCF7F5FC08BD08B5FCF7F1FC08BD08B547 +S31530005B60FCF7EDFC08BD08B5FCF7E9FC08BD08B547 +S31530005B70FCF7E5FC08BD08B5FCF7E1FC08BD08B547 +S31530005B80FCF7DDFC08BD08B5FCF7D9FC08BD08B547 +S31530005B90FCF7D5FC08BD08B5FCF7D1FC08BD08B547 +S31530005BA0FCF7CDFC08BD08B5FCF7C9FC08BD08B547 +S31530005BB0FCF7C5FC08BD08B5FCF7C1FC08BD08B547 +S31530005BC0FCF7BDFC08BD08B5FCF7B9FC08BD08B547 +S31530005BD0FCF7B5FC08BD08B5FCF7B1FC08BD08B547 +S31530005BE0FCF7ADFC08BD08B5FCF7A9FC08BD08B547 +S31530005BF0FCF7A5FC08BD08B5FCF7A1FC08BD08B547 +S31530005C00FCF79DFC08BD08B5FCF799FC08BD08B546 +S31530005C10FCF795FC08BD08B5FCF791FC08BD08B546 +S31530005C20FCF78DFC08BD08B5FCF789FC08BD08B546 +S31530005C30FCF785FC08BD08B5FCF781FC08BD08B546 +S31530005C40FCF77DFC08BD08B5FCF779FC08BD08B546 +S31530005C50FCF775FC08BD08B5FCF771FC08BD08B546 +S31530005C60FCF76DFC08BD08B5FCF769FC08BD08B546 +S31530005C70FCF765FC08BD08B5FCF761FC08BD08B546 +S31530005C80FCF75DFC08BD08B5FCF759FC08BD08B546 +S31530005C90FCF755FC08BD08B5FCF751FC08BD08B546 +S31530005CA0FCF74DFC08BD08B5FCF749FC08BD08B546 +S31530005CB0FCF745FC08BD08B5FCF741FC08BD08B546 +S31530005CC0FCF73DFC08BD08B5FCF739FC08BD08B546 +S31530005CD0FCF735FC08BD08B5FCF731FC08BD08B546 +S31530005CE0FCF72DFC08BD08B5FCF729FC08BD08B546 +S31530005CF0FCF725FC08BD08B5FCF721FC08BD08B546 +S31530005D00FCF71DFC08BD08B5FCF719FC08BD08B545 +S31530005D10FCF715FC08BD08B5FCF711FC08BD08B545 +S31530005D20FCF70DFC08BD08B5FCF709FC08BD08B545 +S31530005D30FCF705FC08BD08B5FCF701FC08BD08B545 +S31530005D40FCF7FDFB08BD08B5FCF7F9FB08BD08B547 +S31530005D50FCF7F5FB08BD08B5FCF7F1FB08BD08B547 +S31530005D60FCF7EDFB08BD08B5FCF7E9FB08BD08B547 +S31530005D70FCF7E5FB08BD08B5FCF7E1FB08BD08B547 +S31530005D80FCF7DDFB08BD08B5FCF7D9FB08BD08B547 +S31530005D90FCF7D5FB08BD08B5FCF7D1FB08BD08B547 +S31530005DA0FCF7CDFB08BD08B5FCF7C9FB08BD08B547 +S31530005DB0FCF7C5FB08BD08B5FCF7C1FB08BD08B547 +S31530005DC0FCF7BDFB08BD08B5FCF7B9FB08BD08B547 +S31530005DD0FCF7B5FB08BD08B5FCF7B1FB08BD08B547 +S31530005DE0FCF7ADFB08BD08B5FCF7A9FB08BD08B547 +S31530005DF0FCF7A5FB08BD08B5FCF7A1FB08BD08B547 +S31530005E00FCF79DFB08BD08B5FCF799FB08BD08B546 +S31530005E10FCF795FB08BD08B5FCF791FB08BD08B546 +S31530005E20FCF78DFB08BD08B5FCF789FB08BD08B546 +S31530005E30FCF785FB08BD08B5FCF781FB08BD08B546 +S31530005E40FCF77DFB08BD08B5FCF779FB08BD08B546 +S31530005E50FCF775FB08BD08B5FCF771FB08BD08B546 +S31530005E60FCF76DFB08BD08B5FCF769FB08BD08B546 +S31530005E70FCF765FB08BD08B5FCF761FB08BD08B546 +S31530005E80FCF75DFB08BD08B5FCF759FB08BD08B546 +S31530005E90FCF755FB08BD08B5FCF751FB08BD08B546 +S31530005EA0FCF74DFB08BD08B5FCF749FB08BD08B546 +S31530005EB0FCF745FB08BD08B5FCF741FB08BD08B546 +S31530005EC0FCF73DFB08BD08B5FCF739FB08BD08B546 +S31530005ED0FCF735FB08BD08B5FCF731FB08BD08B546 +S31530005EE0FCF72DFB08BD08B5FCF729FB08BD08B546 +S31530005EF0FCF725FB08BD08B5FCF721FB08BD08B546 +S31530005F00FCF71DFB08BD08B5FCF719FB08BD0420DE +S31530005F107146084202D0EFF3098001E0EFF30880C2 +S31530005F2081690A884BF6AB639A4200D0FEE70231AC +S31530005F30816120210160704700BF80B582B000AF1B +S31530005F4003463A60FB710B46BB71BA79F8793B6808 +S31530005F500121FCF743FD00BF0837BD4680BD80B543 +S31530005F6084B000AF03460A46FB711346BB71BA795B +S31530005F70F87900230021FCF731FDF860FB681846FC +S31530005F801037BD4680BD80B500AF0020FDF76AF8FA +S31530005F900346184680BD80B584B000AF786007F1FF +S31530005FA0080379681846FDF775FB0346002B06D1C2 +S31530005FB007F108031846FDF797F8002300E00123A0 +S31530005FC018461037BD4680BD80B483B000AF7860C8 +S31530005FD07B681846A0F101000028FBD100BF0C37C2 +S31530005FE0BD465DF8047B704780B582B000AF03468E +S31530005FF0FB71FB7901211846FDF790FD00BF08378C +S31530006000BD4680BD80B584B000AFF860B960134638 +S31530006010FB71FB791A46B968F868FDF74FFE00BF89 +S315300060201037BD4680BD80B582B000AF0346FB71E8 +S31530006030FB7901211846FDF7BFFE00BF0837BD4684 +S3153000604080BD80B483B000AF78607B689B6843F0D6 +S3153000605002027B689A607B689B6823F002027B6849 +S315300060609A6000BF0C37BD465DF8047B704780B43C +S3153000607000AF00BFBD465DF8047B704780B400AF0B +S3153000608000BFBD465DF8047B704780B485B000AF75 +S3153000609078607B68002B02D10023FB8102E042F658 +S315300060A0AF73FB81FB8918461437BD465DF8047B18 +S315300060B0704780B483B000AFEFF310833B603B682A +S315300060C07B6072B600BF7B6818460C37BD465DF8FC +S315300060D0047B704780B485B000AF78607B68FB6026 +S315300060E0FB6883F3108800BF00BF1437BD465DF8E8 +S315300060F0047B704780B586B000AFF860B9607A60CF +S315300061003B6043F22153FB82FB681B78012B08D19D +S31530006110FB680C333A687968184600F0E0F80346B5 +S31530006120FB82FB8A18461837BD4680BD80B586B0DF +S3153000613000AFF860B9607A60FB68002BFCD0BB68B2 +S31530006140002BFCD07B68002BFCD0FB687B617B6925 +S315300061501B683B613B69002BFCD07B68BA68796968 +S315300061603869FFF7C7FF034618461837BD4680BD66 +S3153000617080B584B000AF7860396043F22153FB813B +S315300061803B68002BFCD07B68002BFCD000BF7B68C3 +S31530006190BB6010220021B86800F033F93B681A7AE8 +S315300061A0BB681A703B681B7A012B0BD1BB6803F1B5 +S315300061B00C023B68DB681946104600F071F803465E +S315300061C0FB8100E000BFFB8918461037BD4680BD15 +S315300061D080B586B000AF786039607B68002BFCD024 +S315300061E03B68002BFCD000BF7B687B613B683B6122 +S315300061F0FFF75FFFF8607B699B685A1C7B699A6082 +S31530006200F868FFF767FF04220021386800F0F9F8D4 +S315300062103B697A691A60002318461837BD4680BD37 +S3153000622080B586B000AF786039600023FB827B682A +S31530006230002BFCD03B68002BFCD000BF7B683B6159 +S315300062403B68FB60FFF735FFB8603B695B68002B46 +S3153000625003D043F22253FB820AE03B69FA685A6064 +S3153000626004220021386800F0CCF8FB683A691A60DD +S31530006270B868FFF72FFFFB8A18461837BD4680BD32 +S3153000628080B584B000AFF860B9607A607A68B96872 +S31530006290F868FFF74BFF034618461037BD4680BDFA +S315300062A080B584B000AF786039600023FB813B68ED +S315300062B0002BFCD07B68002BFCD000BF7B68BB601A +S315300062C0BB6839681846FEF729FB0346FB81FB8914 +S315300062D0002BFCD1FB8918461037BD4680BD80B5F2 +S315300062E086B000AFF860B9607A60FB68002BFCD0EE +S315300062F0BB68002BFCD07B68002BFCD0FB687B6135 +S315300063007B697A68B9681846FEF76EFB034618460D +S315300063101837BD4680BD80B500AF1920FEF7C0FBEB +S315300063200346184680BD80B582B000AFFFF7F3FF55 +S3153000633078607B6801224FF4E1310120FCF7AAF93D +S3153000634000BF0837BD4680BD80B483B000AF7860EB +S315300063500B46FB707B685B6923F000527B685A61A1 +S315300063607B685B6823F4F852FB781B0203F4F8531E +S315300063701A437B685B681A437B685A6000BF7B6848 +S315300063809B6803F00043B3F1004FF8D100BF00BF64 +S315300063900C37BD465DF8047B704780B582B000AFE0 +S315300063A00346FB71FB7901211846FEF75DFD00BF00 +S315300063B00837BD4680BD80B485B000AFF860B9609F +S315300063C07A603B60BB6803F00F01FB691B0103F089 +S315300063D01002FB680A431A607B68002B02D07B6888 +S315300063E03A681A6000BF1437BD465DF8047B7047C3 +S315300063F008B5FEF78FFDBDE80840FCF77DBA00F022 +S31530006400EDB900F005BAA8B14FF0805340F80C3C16 +S3153000641050F8042C72B11368B3F1805F0AD150F88A +S31530006420083C516803330B4440F8083C936840F805 +S31530006430043CEDE7704770B5C468A50708D0B2F5DF +S31530006440007F11D0B2F5806F04D0B2F5807F0BD0CB +S31530006450012008E000F1240101232243016101609B +S31530006460C361C260002070BD5E1E6FF07F45AE42D4 +S31530006470F3D3EDE7421C10B58307044616D1234605 +S3153000648054F8041BA1F1013020EA010010F0803FDE +S31530006490F5D011F0FF0F11D011F47F4F0CD011F45D +S315300064A07F0F14BF23460333981A04E010F8013BDC +S315300064B0002BE1D1801A10BD0233F5E70133F3E743 +S315300064C0C368826923F0200310B5C3600446836A2B +S315300064D09A420ED000F048F8E36823F4405323F094 +S315300064E0100343F01003E360A36AA3612369E362F8 +S315300064F02360E36823F4804323F04003E36010BD58 +S3153000650070B50D4606461046144600F056F82B7800 +S31530006510722B17D0772B18D0612B23D1082348F252 +S31530006520020215F8011F2B2912D0622915D01B073C +S31530006530E260666104D5022200212046FEF77EFD28 +S31530006540204670BD00230122EBE704230222E8E750 +S3153000655042F0030243F00203E3E742F0040243F061 +S315300065600103DEE70024EBE738B501680446056928 +S31530006570C36AC06820F40022E26000F08202022A78 +S3153000658002D04FF0FF3038BD10F48030FBD0994246 +S3153000659038BF19468D4208D1E3680020E56223F4FE +S315300065A080332560A060E360EDE72246491B28462C +S315300065B0FEF7ECFC0028EFD0E3E7F0B5C46889B00D +S315300065C00546A10722D0220718D4D0E90467FEF782 +S315300065D013FD384600F035F9230502D53046FFF76E +S315300065E012FFA40DA40514F1A54F07D12022296A64 +S315300065F0684600F035F9684600F00BF83C22002179 +S315300066002846FFF7FEFE002009B0F0BD4FF0FF3000 +S31530006610FAE710B50446FFF72DFF01462046BDE8E0 +S31530006620104000F017B9C36870B513F0030604467E +S3153000663011D09B0611D5856AFFF742FFE3682046E5 +S3153000664023F44053E360FFF78FFF002206462946C6 +S315300066502046FEF7F3FC304670BD816905680D446F +S3153000666001696D1AEAE75FF0000C30B51C0022D1E3 +S3153000667012006FD0914206D305460846002100F03D +S315300066806FF80446284600F06BF80A462146BDE806 +S3153000669030401CF0404F08BF704703D54942404256 +S315300066A061F100015FEA8C0C38BF70475B425242A1 +S315300066B063F10003704742D41C0C04BF1B040CF179 +S315300066C0100C13F07F4F04BF1B020CF1080C13F0B3 +S315300066D0704F04BF1B010CF1040C13F0404F04BF84 +S315300066E00CF1020C5FEA83035CBF0CF1010C5B001A +S315300066F0CCF1200E22FA0EF4234302FA0CF200FA01 +S315300067000CF520FA0EF001FA0CF4204321FA0EF1C2 +S3153000671014461A4600F024F8A4FB0023AA1A71EB9B +S3153000672003033CBF1B190138CCF1200E22FA0CF2C0 +S3153000673003FA0EF4224323FA0CF30021A7E7821A58 +S3153000674071EB03033CBF02460B464FF0000141F1AB +S3153000675000009CE700200021BDE8304000F03DB845 +S315300067600423B1EB122F09D2090241EA106100026B +S31530006770013B08BF7047B1EB122FF5D300184941E2 +S3153000678034BF9142891A4041494134BF9142891AF6 +S315300067904041494134BF9142891A4041494134BF51 +S315300067A09142891A4041494134BF9142891A404148 +S315300067B0494134BF9142891A4041494134BF9142DF +S315300067C0891A4041494134BF9142891A4041A3F167 +S315300067D0010313F00F0FD2D17047704740EA01031F +S315300067E0DB0703460CD40346043A22BF51F804CBE8 +S315300067F043F804CBB2F10402F7D2043208BF704733 +S31530006800013A24BF11F801CB03F801CBF8D8704711 +S31530006810034613F0030F0ED101F0FF0141EA0121C7 +S3153000682041EA0141043A24BF43F8041BB2F10402A1 +S31530006830F9D202F10402013A24BF03F8011BFAE748 +S3153000684070474FF0FF3070474FF0FF3070474FF0D2 +S31530006850FF3070474FF0FF3070474FF0FF307047D2 +S315300068604FF0FF30704710467047FFFF4153534596 +S315300068705254204552524F522022202573200A006E +S315300068802E66736C5F616E61746F705F61692E63C3 +S315300068903A323631203A2066616C736500FFFFFF6D +S315300068A02E66736C5F636C6F636B2E683A32333768 +S315300068B038203A206672657100FFFFFF020503003B +S315300068C007160F1C02050300140F091802050300F2 +S315300068D00F18090D02050300140F091802050300ED +S315300068E018090B11020503001018090D02050300E3 +S315300068F010180B0902050300140F0A1802050300CD +S315300069000910180A020503001018090D02050300C4 +S315300069101018090D020503000F181A0D02050300A1 +S315300069201018090D020503001018090D02050300A1 +S31530006930101813140205030010181A1C0205030060 +S3153000694010181A1C02050300101813140205030050 +S31530006950101813140205030010181314020503004F +S3153000696011090C0F0205030011090C0F0205030073 +S315300069701018090D020503001018090D0205030051 +S31530006980140F0D18020503001018090D0205030037 +S315300069901018090D020503001018090D0205030031 +S315300069A01018090D020503001018090D0205030021 +S315300069B01018090D020503001018090D0205030011 +S315300069C01018090D020503001018090D0205030001 +S315300069D01018090D02050300140F0D1802050300E7 +S315300069E0140F0D18020503001018090D02050300D7 +S315300069F01018090D020503001018090D02050300D1 +S31530006A001018090D02050300140F0D1802050300B6 +S31530006A10140F0D18020503001318090D02050300A3 +S31530006A201318090D020503001318090D020503009A +S31530006A301318090D02050300140F1318020503007D +S31530006A40140F1318020503001018090D0205030070 +S31530006A501018090D02050300171A180B020503005A +S31530006A60171A180B02050300171A180B0205030034 +S31530006A70171A180B02050300171A180B0205030024 +S31530006A80171A180B02050300171A180B0205030014 +S31530006A900C0A1807020503000C0A18070205030042 +S31530006AA018101A0D0205030018101A0D02050300FE +S31530006AB0140F1A18020503001A0F130D02050300EE +S31530006AC01A13180D020503001A13180D02050300D8 +S31530006AD01A13180D02050300140F1A1802050300C5 +S31530006AE0090B0F1C02050300090C111C02050300DB +S31530006AF0090C111C02050300090A111C02050300CA +S31530006B00090A111C020503000C0F0A1C02050300BA +S31530006B100C0F0A1C020503000C0F0A1C02050300A9 +S31530006B200C0F121C020503000C0912180205030093 +S31530006B300D01121A2E66736C5F636C6F636B2E6376 +S31530006B403A3636203A2028636F6E6669672D3E6C7A +S31530006B506F6F7044697669646572203C3D2041529E +S31530006B604D5F504C4C5F4449565F53454C5F4D41E9 +S31530006B7058292026262028636F6E6669672D3E6C5D +S31530006B806F6F7044697669646572203E3D2041526C +S31530006B904D5F504C4C5F4449565F53454C5F4D49B1 +S31530006BA04E2900FF2E66736C5F636C6F636B2E63CA +S31530006BB03A323231203A2066726163203C3D2050B1 +S31530006BC046445F465241435F4D4158202626206653 +S31530006BD0726163203E3D205046445F465241435FDA +S31530006BE04D494E002E66736C5F636C6F636B2E631C +S31530006BF03A323334203A2066616C736500FFFFFF0A +S31530006C002E66736C5F636C6F636B2E633A32353608 +S31530006C10203A2028706C6C203D3D206B434C4F430E +S31530006C204B5F506C6C5379733229207C7C202870F2 +S31530006C306C6C203D3D206B434C4F434B5F506C6C2E +S31530006C4053797333290000002E66736C5F636C6F63 +S31530006C50636B2E633A323639203A2066616C73653F +S31530006C60000000002E66736C5F636C6F636B2E637F +S31530006C703A323734203A202866726163203E3D200E +S31530006C805046445F465241435F4D494E29202626A1 +S31530006C90202866726163203C3D205046445F465250 +S31530006CA041435F4D415829002E66736C5F636C6FAC +S31530006CB0636B2E633A353430203A206366670000C2 +S31530006CC02E66736C5F636C6F636B2E633A3534324A +S31530006CD0203A202866726571496E4D687A203C3DAF +S31530006CE020726566467265712920262620286672CE +S31530006CF06571496E4D687A203E3D20313536290022 +S31530006D002E66736C5F636C6F636B2E633A383738FD +S31530006D10203A2028706C6C203D3D206B434C4F430D +S31530006D204B5F506C6C417564696F29207C7C2028E0 +S31530006D30706C6C203D3D206B434C4F434B5F506C29 +S31530006D406C566964656F29002E66736C5F636C6F71 +S31530006D50636B2E633A393532203A2066616C73653F +S31530006D60000000002E66736C5F636C6F636B2E637E +S31530006D703A393535203A2066726571002E66736C65 +S31530006D805F636C6F636B2E633A31303637203A204F +S31530006D9066616C73650000002E66736C5F636C6FA2 +S31530006DA0636B2E633A31303730203A206672657124 +S31530006DB000FFFFFF2E66736C5F636F6D6D6F6E2E17 +S31530006DC0633A323438203A20305520213D206465EC +S31530006DD06C61795F757300002E66736C5F636F6DDF +S31530006DE06D6F6E2E633A323530203A20636F756E92 +S31530006DF074203C3D2055494E5433325F4D41580046 +S31530006E000000000000C01240000013400040134054 +S31530006E100080134000C013400000144000C0C5403D +S31530006E200000C6400040C6400080C64000C0C64094 +S31530006E300000C7400000CA400080004200C0004247 +S31530006E408A33333333333333333333333333FFFFED +S31530006E502E66736C5F6770696F2E633A3537203AEA +S31530006E6020696E7374616E6365203C2041525241D5 +S31530006E70595F53495A4528735F6770696F4261732A +S31530006E80657329002E66736C5F6770696F2E633A7F +S31530006E90313133203A2070696E203C203332550030 +S31530006EA00000000000C007400000084000400840D5 +S31530006EB00080084000C008400000094000400940FA +S31530006EC00080094000C0094000000A400040C2402E +S31530006ED00080C2408A565758595A5B5C5D5E5F6087 +S31530006EE061FFFFFF2E66736C5F6C70756172742E76 +S31530006EF0633A313236203A20696E7374616E636557 +S31530006F00203C2041525241595F53495A4528735FBC +S31530006F106C707561727442617365732900FFFFFF8F +S31530006F202E66736C5F6C70756172742E633A323490 +S31530006F3035203A204E554C4C20213D20636F6E66ED +S31530006F40696700002E66736C5F6C70756172742EA3 +S31530006F50633A323436203A203055203C20636F6E07 +S31530006F606669672D3E62617564526174655F427011 +S31530006F70730000002E66736C5F6C70756172742ED0 +S31530006F80633A323438203A202875696E74385F7423 +S31530006F902946534C5F464541545552455F4C5055F2 +S31530006FA04152545F4649464F5F53495A456E2862AF +S31530006FB061736529203E3D20636F6E6669672D3E9D +S31530006FC074784669666F57617465726D61726B006D +S31530006FD02E66736C5F6C70756172742E633A3234E0 +S31530006FE039203A202875696E74385F742946534CB7 +S31530006FF05F464541545552455F4C50554152545F5A +S315300070004649464F5F53495A456E28626173652932 +S31530007010203E3D20636F6E6669672D3E7278466905 +S31530007020666F57617465726D61726B002E66736C34 +S315300070305F6C70756172742E633A353231203A2046 +S315300070404E554C4C20213D20636F6E66696700FFBC +S315300070502E66736C5F6C70756172742E633A383756 +S3153000706038203A204E554C4C20213D2064617461C5 +S3153000707000FFFFFF2E66736C5F706D752E633A38B6 +S315300070803638203A20636F6E66696720213D204E80 +S31530007090554C4C002E66736C5F706D752E633A38A6 +S315300070A03836203A20636F6E66696720213D204E60 +S315300070B0554C4C000000000000C00740000008405E +S315300070C0004008400080084000C0084000000940E9 +S315300070D0004009400080094000C0094000000A40D5 +S315300070E00040C2400080C2402E66736C5F616461AE +S315300070F0707465725F6C70756172742E633A323576 +S3153000710034203A2068616E646C6500002E66736CBC +S315300071105F616461707465725F6C70756172742ED4 +S31530007120633A323535203A20636F6E666967000000 +S315300071302E66736C5F616461707465725F6C7075B6 +S315300071406172742E633A323536203A20636F6E663A +S3153000715069672D3E696E7374616E6365203C2028C5 +S3153000716073697A656F6628735F4C707561727441A6 +S315300071706461707465724261736529202F2073696A +S315300071807A656F66284C50554152545F5479706514 +S31530007190202A2929000000002E66736C5F61646125 +S315300071A0707465725F6C70756172742E633A3235C5 +S315300071B037203A20735F4C70756172744164617028 +S315300071C0746572426173655B636F6E6669672D3E87 +S315300071D0696E7374616E63655D00FFFF2E66736C56 +S315300071E05F616461707465725F6C70756172742E04 +S315300071F0633A333534203A2068616E646C6500003A +S315300072002E66736C5F616461707465725F6C7075E5 +S315300072106172742E633A333535203A206461746175 +S31530007220000000002E66736C5F6164617074657275 +S315300072305F6C70756172742E633A333536203A203E +S315300072406C656E67746800FF433A5C55736572739C +S315300072505C6E786130363534385C446F63756D6595 +S315300072606E74735C4D43555870726573736F4944D1 +S31530007270455F31312E332E305F353135385F7072A0 +S315300072806332202D20436F70795C776F726B737029 +S315300072906163655C65766B6D696D787274313137B3 +S315300072A0305F696C65645F626C696E6B795F636D64 +S315300072B0375F515350495F464C4153485C6472695D +S315300072C0766572732F66736C5F636C6F636B2E6853 +S315300072D03A32333738203A206672657100FFFFFF45 +S315300072E00205030007160F1C02050300140F0918C8 +S315300072F0020503000F18090D02050300140F0918C3 +S315300073000205030018090B11020503001018090DB8 +S315300073100205030010180B0902050300140F0A18A2 +S31530007320020503000910180A020503001018090D9A +S31530007330020503001018090D020503000F181A0D77 +S31530007340020503001018090D020503001018090D77 +S3153000735002050300101813140205030010181A1C36 +S315300073600205030010181A1C020503001018131426 +S315300073700205030010181314020503001018131425 +S315300073800205030011090C0F0205030011090C0F49 +S31530007390020503001018090D020503001018090D27 +S315300073A002050300140F0D18020503001018090D0D +S315300073B0020503001018090D020503001018090D07 +S315300073C0020503001018090D020503001018090DF7 +S315300073D0020503001018090D020503001018090DE7 +S315300073E0020503001018090D020503001018090DD7 +S315300073F0020503001018090D02050300140F0D18BD +S3153000740002050300140F0D18020503001018090DAC +S31530007410020503001018090D020503001018090DA6 +S31530007420020503001018090D02050300140F0D188C +S3153000743002050300140F0D18020503001318090D79 +S31530007440020503001318090D020503001318090D70 +S31530007450020503001318090D02050300140F131853 +S3153000746002050300140F1318020503001018090D46 +S31530007470020503001018090D02050300171A180B30 +S3153000748002050300171A180B02050300171A180B0A +S3153000749002050300171A180B02050300171A180BFA +S315300074A002050300171A180B02050300171A180BEA +S315300074B0020503000C0A1807020503000C0A180718 +S315300074C00205030018101A0D0205030018101A0DD4 +S315300074D002050300140F1A18020503001A0F130DC4 +S315300074E0020503001A13180D020503001A13180DAE +S315300074F0020503001A13180D02050300140F1A189B +S3153000750002050300090B0F1C02050300090C111CB0 +S3153000751002050300090C111C02050300090A111C9F +S3153000752002050300090A111C020503000C0F0A1C90 +S31530007530020503000C0F0A1C020503000C0F0A1C7F +S31530007540020503000C0F121C020503000C09121869 +S31530007550020503000D01121A433A5C5573657273C6 +S315300075605C6E786130363534385C446F63756D6582 +S315300075706E74735C4D43555870726573736F4944BE +S31530007580455F31312E332E305F353135385F70728D +S315300075906332202D20436F70795C776F726B737016 +S315300075A06163655C65766B6D696D787274313137A0 +S315300075B0305F696C65645F626C696E6B795F636D51 +S315300075C0375F515350495F464C4153485C6472694A +S315300075D0766572732F66736C5F636C6F636B2E6840 +S315300075E03A32323833203A20636F6E66696700FF6D +S315300075F00205030007160F1C02050300140F0918B5 +S31530007600020503000F18090D02050300140F0918AF +S315300076100205030018090B11020503001018090DA5 +S315300076200205030010180B0902050300140F0A188F +S31530007630020503000910180A020503001018090D87 +S31530007640020503001018090D020503000F181A0D64 +S31530007650020503001018090D020503001018090D64 +S3153000766002050300101813140205030010181A1C23 +S315300076700205030010181A1C020503001018131413 +S315300076800205030010181314020503001018131412 +S315300076900205030011090C0F0205030011090C0F36 +S315300076A0020503001018090D020503001018090D14 +S315300076B002050300140F0D18020503001018090DFA +S315300076C0020503001018090D020503001018090DF4 +S315300076D0020503001018090D020503001018090DE4 +S315300076E0020503001018090D020503001018090DD4 +S315300076F0020503001018090D020503001018090DC4 +S31530007700020503001018090D02050300140F0D18A9 +S3153000771002050300140F0D18020503001018090D99 +S31530007720020503001018090D020503001018090D93 +S31530007730020503001018090D02050300140F0D1879 +S3153000774002050300140F0D18020503001318090D66 +S31530007750020503001318090D020503001318090D5D +S31530007760020503001318090D02050300140F131840 +S3153000777002050300140F1318020503001018090D33 +S31530007780020503001018090D02050300171A180B1D +S3153000779002050300171A180B02050300171A180BF7 +S315300077A002050300171A180B02050300171A180BE7 +S315300077B002050300171A180B02050300171A180BD7 +S315300077C0020503000C0A1807020503000C0A180705 +S315300077D00205030018101A0D0205030018101A0DC1 +S315300077E002050300140F1A18020503001A0F130DB1 +S315300077F0020503001A13180D020503001A13180D9B +S31530007800020503001A13180D02050300140F1A1887 +S3153000781002050300090B0F1C02050300090C111C9D +S3153000782002050300090C111C02050300090A111C8C +S3153000783002050300090A111C020503000C0F0A1C7D +S31530007840020503000C0F0A1C020503000C0F0A1C6C +S31530007850020503000C0F121C020503000C09121856 +S31530007860020503000D01121A0300000072007700B2 +S3093000787000A4781FA3 +S705300024E1C5 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd new file mode 100644 index 00000000..331527e8 --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd @@ -0,0 +1,14 @@ +options { + flags = 0x00; + startAddress = 0x2000; + ivtOffset = 0x400; + initialLoadSize = 0x1000; + entryPointAddress = 0x34e1; +} + +sources { + elfFile = extern(0); +} + +section (0) { +} \ No newline at end of file diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bin b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bin new file mode 100644 index 0000000000000000000000000000000000000000..908a663e334a9705df6168f7c38858fd53e75229 GIT binary patch literal 25708 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 GN&gPyTn)$o literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19 b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19 new file mode 100644 index 00000000..e814cad6 --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19 @@ -0,0 +1,1418 @@ +S02B000065766B6D696D787274313137305F696C65645F626C696E6B795F636D375F696E745F52414D2E73311E +S113300000000400E13400002D3500000F6F0000C3 +S11330103135000033350000353500000000000074 +S11330200000000000000000000000003735000030 +S113303039350000000000003B350000A9380000CD +S11330403F680000476800004F68000057680000B0 +S11330505F680000676800006F6800007768000020 +S11330607F680000876800008F6800009768000090 +S11330709F680000A7680000AF680000B768000000 +S1133080BF680000C7680000CF680000D768000070 +S1133090DF680000E7680000EF680000F7680000E0 +S11330A0FF680000076900000F690000176900004D +S11330B01F690000276900002F69000037690000BC +S11330C03F690000476900004F690000576900002C +S11330D05F690000676900006F690000776900009C +S11330E07F690000876900008F690000976900000C +S11330F09F690000A7690000AF690000B76900007C +S1133100BF690000C7690000CF690000D7690000EB +S1133110DF690000E7690000EF690000F76900005B +S1133120FF690000076A00000F6A0000176A0000C8 +S11331301F6A0000276A00002F6A0000376A000037 +S11331403F6A0000476A00004F6A0000576A0000A7 +S11331505F6A0000676A00006F6A0000776A000017 +S11331607F6A0000876A00008F6A0000976A000087 +S11331709F6A0000A76A0000AF6A0000B76A0000F7 +S1133180BF6A0000C76A0000CF6A0000D76A000067 +S1133190DF6A0000E76A0000EF6A0000F76A0000D7 +S11331A0FF6A0000076B00000F6B0000176B000044 +S11331B01F6B0000276B00002F6B0000376B0000B3 +S11331C03F6B0000476B00004F6B0000576B000023 +S11331D05F6B0000676B00006F6B0000776B000093 +S11331E07F6B0000876B00008F6B0000976B000003 +S11331F09F6B0000A76B0000AF6B0000B76B000073 +S1133200BF6B0000C76B0000CF6B0000D76B0000E2 +S1133210DF6B0000E76B0000EF6B0000F76B000052 +S1133220FF6B0000076C00000F6C0000176C0000BF +S11332301F6C0000276C00002F6C0000376C00002E +S11332403F6C0000476C00004F6C0000576C00009E +S11332505F6C0000676C00006F6C0000776C00000E +S11332607F6C0000876C00008F6C0000976C00007E +S11332709F6C0000A76C0000AF6C0000B76C0000EE +S1133280BF6C0000C76C0000CF6C0000D76C00005E +S1133290DF6C0000E76C0000EF6C0000F76C0000CE +S11332A0FF6C0000076D00000F6D0000176D00003B +S11332B01F6D0000276D00002F6D0000376D0000AA +S11332C03F6D0000476D00004F6D0000576D00001A +S11332D05F6D0000676D00006F6D0000776D00008A +S11332E07F6D0000876D00008F6D0000976D0000FA +S11332F09F6D0000A76D0000AF6D0000B76D00006A +S1133300BF6D0000C76D0000CF6D0000D76D0000D9 +S1133310DF6D0000E76D0000EF6D0000F76D000049 +S1133320FF6D0000076E00000F6E0000176E0000B6 +S11333301F6E0000276E00002F6E0000376E000025 +S11333403F6E0000476E00004F6E0000576E000095 +S11333505F6E0000676E00006F6E0000776E000005 +S11333607F6E0000876E00008F6E0000976E000075 +S11333709F6E0000A76E0000AF6E0000B76E0000E5 +S1133380BF6E0000C76E0000CF6E0000D76E000055 +S1133390DF6E0000E76E0000EF6E0000F76E0000C5 +S11333A0FF6E0000076F0000688800006888000056 +S11333B004000000688800000000002000000000F5 +S11333C068880000000024200000000068880000D5 +S11333D000002C2000000000688800000000342059 +S11333E00000000068880000000035200000000094 +S11333F06888000000000080000000006888000069 +S113340000000083000000006C8800001801000028 +S11334100000002000000000000024200000000044 +S113342000002C20000000000000342000000000F8 +S113343000003520000000000000008000000000B3 +S1133440000000830000000030B40B4600249442C6 +S113345005D250F8045B43F8045B0434F7E70C4BE3 +S11334605B6913F4803F11D0002A0FDD01F01F03C4 +S11334701A44BFF34F8F064BC3F868122031203A29 +S1133480002AF8DCBFF34F8FBFF36F8F30BC704757 +S113349000ED00E010B4034600228A4204D2002466 +S11334A043F8044B0432F8E70C4B5B6913F4803F98 +S11334B011D000290FDD00F01F031944BFF34F8F13 +S11334C0064BC3F86802203020390029F8DCBFF32A +S11334D04F8FBFF36F8F5DF8044B704700ED00E032 +S11334E010B572B602F000F90D4B07E003F10C04BD +S11334F09A6859681868FFF7A7FF2346094A934258 +S1133500F4D306E01C46596854F8080BFFF7C2FFD1 +S11335102346054A9342F5D362B603F069FFFEE7FA +S1133520A83300000834000048340000FEE7FEE73A +S1133530FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE75F +S113354080B58AB000AF0346FB7100237B6297F914 +S11335500730042B06D097F90730052B02D03248E8 +S113356000F064F897F90730042B01D1022300E03E +S113357003230021184603F0F2FC38623B6A5B0E19 +S113358003F00703FB613B6A03F07F033B6297F997 +S11335900730042B01D1022300E003233021184615 +S11335A003F0DDFC07EE900AB8EE677B87ED047B41 +S11335B097F90730042B01D1022300E003232021D3 +S11335C0184603F0CCFC07EE900AB8EE677B87ED53 +S11335D0027B3B6A07EE903AB8EE676B97ED024BBD +S11335E097ED045B84EE057B36EE077B9FED0C6B59 +S11335F027EE065B0122FB6902FA03F307EE903A19 +S1133600B8EEE76B85EE067BFCEEC77B17EE903ACF +S11336107B627B6A18462837BD4680BDAFF30080C5 +S11336200000000060E37641007D000080B582B0B8 +S113363000AF78607968024800F0AEF800BEFDE79C +S11336406C78000080B584B000AF786039607B6826 +S1133650002BFCD03B68002BFCD00C4B1B68002BD0 +S113366001D100230DE03A687968094803F008FEA7 +S11336700346FB81FB89002B01D13B6801E04FF03D +S1133680FF3318461037BD4680BD00BF8888000050 +S11336908088000080B58EB000AFB9607B600346BF +S11336A0FB731346BB730023FB867B687B61BB689B +S11336B0BB6100233B7700237B770123BB77012386 +S11336C0FB77FB7B87F82030BB7B87F82C3048F6F0 +S11336D04173FB85BB7B012B03D107F114033B63CF +S11336E017E0BB7B022B03D143F22153FB8610E08E +S11336F0BB7B032B03D143F22153FB8609E0BB7B45 +S1133700042B03D143F22153FB8602E043F22153FD +S1133710FB86FB8E43F2215293422ED01C220021C1 +S1133720184803F06EFE174B174A1A60154B1B68B6 +S113373007F124021146184603F01AFD0346FB86DE +S1133740FB8E002BFCD10F4B1B681049184603F06D +S11337503FFD0346FB86FB8E002BFCD1094B1B6807 +S11337600B49184603F05CFD0346FB86FB8E002BD9 +S1133770FCD1044B1B68074A1360FB8E184638378C +S1133780BD4680BD6C880000708800008088000001 +S113379084880000888800000FB480B5A4B000AF0E +S11337A00023C7F888300023C7F88C3000237B60DF +S11337B007F108037C220021184603F022FE124B75 +S11337C01B68002B16D007F19C03C7F884303A1D00 +S11337D00E4BD7F88410D7F8980002F0E8FEC7F82B +S11337E08800D7F888203B1D11461846FFF72AFFAA +S11337F0C7F88C00D7F88C3018469037BD46BDE822 +S1133800804004B0704700BF8888000081610000D8 +S113381080B483B000AF03463960FB80B7F906304B +S1133820002B0ADB3B68DAB20C49B7F90630120107 +S1133830D2B20B4483F800230AE03B68DAB20849A9 +S1133840FB8803F00F03043B1201D2B20B441A7637 +S113385000BF0C37BD465DF8047B704700E100E013 +S113386000ED00E080B582B000AF78607B68013B7A +S1133870B3F1807F01D301230FE00A4A7B68013B47 +S113388053600F214FF0FF30FFF7C2FF054B0022BA +S11338909A60044B07221A60002318460837BD4675 +S11338A080BD00BF10E000E080B400AF064B1B6891 +S11338B0002B04D0044B1B68013B034A136000BF78 +S11338C0BD465DF8047B70478C88000080B483B0EB +S11338D000AF7860074A7B68136000BF054B1B6824 +S11338E0002BFBD100BF00BF0C37BD465DF8047B45 +S11338F0704700BF8C88000080B500AF02F0E4FA86 +S113390002F0BEF903F00FFD03F0B1FB134B1B688B +S1133910134AA2FB03239B091846FFF7A3FF0346A0 +S1133920002B00D0FEE74FF47A70FFF7CFFF0D4B6A +S11339301B78DBB2002B08D0002203210A4801F0D7 +S113394093F9084B00221A70EDE70122032106487F +S113395001F08AF9034B01221A70E4E76888000039 +S1133960D34D6210808900000040C64080B582B00B +S113397000AF0021002003F0F2FA78607B6840F287 +S11339800512934222D040F205120021002003F0D8 +S1133990D4FA0021002003F0E2FA0E4B4FF48072B7 +S11339A0C3F880250B4BD3F820380A4A23F480331C +S11339B0C2F820380849012001F064F8054BD3F817 +S11339C07035044A43F01003C2F8703500BF08375D +S11339D0BD4680BD0040C84000CA9A3B80B586B051 +S11339E000AF3B600346FB710B46BB7113467B7112 +S11339F0FB79062B00F2008301A252F823F000BFEA +S1133A001D3A0000C93A0000B53B0000B53C000077 +S1133A10B13D00009D3E00004D3F0000BB79002BEE +S1133A2024D0A34BD3F82038A14A23F48033C2F81E +S1133A3020389F4BD3F820387B617B6923F0FF0348 +S1133A407B617B797A6913437B61994A7B69C2F80C +S1133A502038974A3B68C2F83038954BD3F8003584 +S1133A60934A83F48033C2F80035C8E2904BD3F80C +S1133A7020387B617B6923F480337B617B6943F469 +S1133A8080337B618A4A7B69C2F82038884BD3F83B +S1133A9020387B617B6923F0FF037B617B797A6942 +S1133AA013437B61824A7B69C2F82038804BD3F888 +S1133AB000357F4A83F48033C2F800357C4BD3F859 +S1133AC040383B613B699BE2BB79002B34D0784B97 +S1133AD0D3F8503803F40073FB60754BD3F85038B7 +S1133AE0734A23F48033C2F85038714BD3F85038FA +S1133AF07B617B6923F0FF037B617B797A691343E4 +S1133B007B616B4A7B69C2F85038694A3B68C2F8EA +S1133B106038674BD3F85038654A83F48073C2F831 +S1133B205038634BD3F8503803F40073BB60BA6861 +S1133B30FB689A42F5D062E25D4BD3F8503803F447 +S1133B400073FB605A4BD3F850387B617B6923F4D4 +S1133B5080337B617B6943F480337B61544A7B69A6 +S1133B60C2F85038524BD3F850387B617B6923F04C +S1133B70FF037B617B797A6913437B614C4A7B69E0 +S1133B80C2F850384A4BD3F85038494A83F480730A +S1133B90C2F85038464BD3F8503803F40073BB6076 +S1133BA0BA68FB689A42F5D0414BD3F870383B6150 +S1133BB03B6925E2BB79002B3CD03D4BD3F88038E0 +S1133BC003F40073FB603A4BD3F88038384A23F48B +S1133BD08033C2F88038364BD3F880387B617B69F8 +S1133BE023F0FF037B617B797A6913437B61304A5D +S1133BF07B69C2F880382E4A3B68C2F890382C4B57 +S1133C00D3F880382A4A83F48073C2F88038284B6A +S1133C10D3F8803803F40073BB60BA68FB689A4237 +S1133C20F5D0234BD3F88038214A23F48033C2F8EB +S1133C308038E4E11E4BD3F8803803F40073FB6052 +S1133C401B4BD3F880387B617B6923F480337B6121 +S1133C507B6943F480337B61154A7B69C2F8803801 +S1133C60134BD3F880387B617B6923F0FF037B61BE +S1133C707B797A6913437B610D4A7B69C2F880388A +S1133C800B4BD3F880380A4A83F48073C2F8803827 +S1133C90074BD3F8803803F40073BB60BA68FB6841 +S1133CA09A42F5D0024BD3F8A0383B613B69A7E1B7 +S1133CB00040C840BB79002B3CD0A34BD3F8B038AC +S1133CC003F40073FB60A04BD3F8B0389E4A23F48E +S1133CD08033C2F8B0389C4BD3F8B0387B617B6931 +S1133CE023F0FF037B617B797A6913437B61964AF6 +S1133CF07B69C2F8B038944A3B68C2F8C038924B2A +S1133D00D3F8B038904A83F48073C2F8B0388E4B3D +S1133D10D3F8B03803F40073BB60BA68FB689A4206 +S1133D20F5D0894BD3F8B038874A23F48033C2F8EE +S1133D30B03864E1844BD3F8B03803F40073FB600B +S1133D40814BD3F8B0387B617B6923F480337B618A +S1133D507B6943F480337B617B4A7B69C2F8B0386A +S1133D60794BD3F8B0387B617B6923F0FF037B6127 +S1133D707B797A6913437B61734A7B69C2F8B038F3 +S1133D80714BD3F8B038704A83F48073C2F8B038FA +S1133D906D4BD3F8B03803F40073BB60BA68FB68AA +S1133DA09A42F5D0684BD3F8D0383B613B6927E1A0 +S1133DB0BB79002B34D0644BD3F8203903F400735F +S1133DC0FB60614BD3F820395F4A23F48033C2F897 +S1133DD020395D4BD3F820397B617B6923F0FF03E5 +S1133DE07B617B797A6913437B61574A7B69C2F8AB +S1133DF02039554A3B68C2F83039534BD3F820393F +S1133E00514A83F48073C2F820394F4BD3F82039D8 +S1133E1003F40073BB60BA68FB689A42F5D0EEE025 +S1133E20494BD3F8203903F40073FB60464BD3F8B5 +S1133E3020397B617B6923F480337B617B6943F4A4 +S1133E4080337B61404A7B69C2F820393E4BD3F80A +S1133E5020397B617B6923F0FF037B617B797A697D +S1133E6013437B61384A7B69C2F82039364BD3F857 +S1133E702039354A83F48073C2F82039324BD3F8A1 +S1133E80203903F40073BB60BA68FB689A42F5D02A +S1133E902D4BD3F840393B613B69B1E0BB79002B32 +S1133EA024D0294BD3F8E038274A23F48033C2F8CE +S1133EB0E038254BD3F8E0387B617B6923F0FF03BE +S1133EC07B617B797A6913437B611F4A7B69C2F802 +S1133ED0E0381D4A3B68C2F8F0381B4BD3F8003475 +S1133EE0194A83F40043C2F8003488E0164BD3F82F +S1133EF0E0387B617B6923F480337B617B6943F425 +S1133F0080337B61104A7B69C2F8E0380E4BD3F8EA +S1133F10E0387B617B6923F0FF037B617B797A69FD +S1133F2013437B61084A7B69C2F8E038064BD3F837 +S1133F300034054A83F40043C2F80034024BD3F83A +S1133F4010393B613B695BE00040C840BB79002B02 +S1133F5024D02D4BD3F8E0382B4A23F48033C2F815 +S1133F60E038294BD3F8E0387B617B6923F0FF0309 +S1133F707B617B797A6913437B61234A7B69C2F84D +S1133F80E038214A3B68C2F8F0381F4BD3F870354B +S1133F901D4A83F00103C2F8703530E01A4BD3F8A0 +S1133FA0E0387B617B6923F480337B617B6943F474 +S1133FB080337B61144A7B69C2F8E038124BD3F832 +S1133FC0E0387B617B6923F0FF037B617B797A694D +S1133FD013437B610C4A7B69C2F8E0380A4BD3F87F +S1133FE07035094A83F00103C2F87035064BD3F8E3 +S1133FF000393B613B6903E00448FFF717FB0023EA +S113400018461837BD4680BD0040C840807800007F +S113401080B483B000AF0346FB71074AFB79DB0130 +S113402013441B681B0A03F0070318460C37BD46EC +S11340305DF8047B704700BF0000CC4080B483B0BF +S113404000AF0346FB71064AFB79DB0113441B688E +S1134050DBB2013318460C37BD465DF8047B70476C +S11340600000CC4090B5ADF5237D00AF0246FB1DAA +S11340701A7007F108031A4A184611464FF41E73C2 +S11340801A4603F0BCF9FB1D1B781846FFF7C0FF66 +S1134090C7F88402FB1D1B7807F10802DB001A44F1 +S11340A0D7F8843213441B78184600F003FC044606 +S11340B0FB1D1B781846FFF7C1FF0346B4FBF3F35F +S11340C0C7F88032D7F88032002B02D10548FFF7B9 +S11340D0ADFAD7F88032184607F52377BD4690BD70 +S11340E0BC780000A078000080B584B000AF786090 +S11340F07B685B68D02B03D87B685B68672B02D82E +S11341004848FFF793FA484BD3F8003203F40053BE +S1134110002B34D0444BD3F80032DA437B685B681D +S11341201340DBB2002B2AD13F4BD3F80032DA43E1 +S11341307B681B78DB03134003F4C033002B1ED1D0 +S1134140394BD3F8003203F48043002B07D1364BAC +S1134150D3F80032344A43F48043C2F80032324B7D +S1134160D3F8003203F08043002B56D02E4BD3F803 +S113417000322D4A23F08043C2F800324DE0FFF7AD +S1134180F5FB294BD3F8003223F00053FB60FB68A6 +S113419003F4C043002B0BD0FB6823F4C043FB6043 +S11341A0FB6843F08043FB601F4AFB68C2F800329F +S11341B0FA681E4B1340FB607B685B68DAB27B686D +S11341C01B78DB0303F4C0331A43FB681A43184B10 +S11341D01343FB60144AFB68C2F80032BFF34F8FED +S11341E000BFBFF36F8F00BF12491E2000F04AFCCE +S11341F000BF0D4BD3F8003203F00053002BF8D06E +S1134200FB6843F48043FB60FB6823F08043FB605E +S1134210054AFB68C2F8003200E000BF1037BD4613 +S113422080BD00BF347B00000040C840007FFEFF1B +S11342300020004000CA9A3B80B584B000AF78608B +S1134240524BD3F8403203F40003002B2CD04F4BD5 +S1134250D3F880227B685B689A4225D14B4BD3F814 +S113426090227B689B689A421ED1484BD3F8403217 +S113427003F40053002B07D1444BD3F84032434A94 +S113428043F40053C2F84032404BD3F8403203F0B9 +S11342908043002B76D03D4BD3F840323B4A23F089 +S11342A08043C2F840326DE0FFF760FB374BD3F830 +S11342B07032364A43F08033C2F87032334BD3F84D +S11342C04032FB60FA68324B1340002B0BD0FA6883 +S11342D0304B1340FB60FB6843F08043FB602B4A88 +S11342E0FB68C2F84032294B6FF07042C3F8A02239 +S11342F0264A7B685B68C2F88032244A7B689B68E4 +S1134300C2F89032244BFB60204AFB68C2F840326A +S113431022491E2000F0B6FBFA68214B1343FB60D0 +S11343201A4AFB68C2F840321C49FA2000F0AAFB82 +S1134330FB6823F40063FB60144AFB68C2F8403254 +S113434000BF124BD3F8403203F00053B3F1005FC7 +S1134350F7D1FB6843F40053FB600C4AFB68C2F8D6 +S11343604032FB6823F08043FB60084AFB68C2F8D4 +S11343704032064BD3F87032044A03F07F33C2F85C +S1134380703200E000BF1037BD4680BD0040C84019 +S113439000208000FFDF7FFF0800004000CA9A3B36 +S11343A00008800080B586B000AF0346FB710B4661 +S11343B0BB7113467B7100237B6100233B617B79D6 +S11343C0232B02D87B790B2B02D83348FFF72EF925 +S11343D097F90730022B02D0032B05D009E02F4BAD +S11343E07B612F4B3B6107E02E4B7B612E4B3B6186 +S11343F002E02E48FFF71AF97B691A68BB79DB00E3 +S1134400402101FA03F31340FB607B691A68BB790E +S1134410DB00802101FA03F31A437B691A607B698C +S11344201A68BB79DB003F2101FA03F3DB431A402E +S11344307B691A607B691A687B7903F03F01BB7959 +S1134440DB0001FA03F31A437B691A603B691A68BB +S1134450BB79022101FA03F35A403B691A607B6974 +S11344601A68BB79DB00802101FA03F3DB431A40AD +S11344707B691A6000BF7B691A68BB79DB00402145 +S113448001FA03F31A40FB689A42F4D000BF00BF5C +S11344901837BD4680BD00BFA47B00007042C840F1 +S11344A05042C8403042C8402042C840E47B00002B +S11344B080B584B000AF03460A46FB711346BB7156 +S11344C00023FB600023BB6097F90730022B06D062 +S11344D097F90730032B02D02348FFF7A7F897F981 +S11344E00730022B02D0032B0DD019E01F4BD3F859 +S11344F07022BB79DB003F2101FA03F31340BB6058 +S11345001B4BFB600FE0194BD3F83022BB79DB0067 +S11345103F2101FA03F31340BB60164BFB6002E03A +S11345201548FFF783F8BB79DB00BA6822FA03F376 +S1134530BB60BB680B2B02D9BB68232B02D90F4885 +S1134540FFF774F8BB68002B08D0FA68BB68B2FBAD +S1134550F3F21346DB0013445B0000E0002318462B +S11345601037BD4680BD00BF007C00000040C8403D +S113457000A4781F00389C1C487C0000647C000068 +S113458080B584B000AF78603E4BD3F8103203F4AA +S11345900013002B29D03B4BD3F81032DA437B684D +S11345A01B68134003F00703002B1ED1354BD3F8CF +S11345B0103203F40053002B07D1324BD3F81032DE +S11345C0304A43F40053C2F810322E4BD3F8103261 +S11345D003F08043002B50D02A4BD3F81032294AE1 +S11345E023F08043C2F8103247E0FFF7BFF9254BB0 +S11345F0D3F83032234A43F08033C2F830327B6838 +S11346001B6803F00702204B1343FB601D4AFB6841 +S1134610C2F810321D491E2000F034FAFA681C4B0F +S11346201343FB60174AFB68C2F8103217491E2077 +S113463000F028FAFB6823F40063FB60114AFB686E +S1134640C2F8103200BF0F4BD3F8103203F00053FE +S1134650B3F1005FF7D1FA6842F208031343FB6039 +S1134660084AFB68C2F81032FB6823F08043FB6001 +S1134670044AFB68C2F8103200E000BF1037BD46A0 +S113468080BD00BF0040C8401000004000CA9A3BF3 +S11346900008200080B584B000AF786039607B6882 +S11346A0002B02D13348FEF7C1FF4FF41C63FB60BB +S11346B03A68FB689A4202D83B689B2B02D82E4882 +S11346C0FEF7B4FF3A68FB689A4206D37B6803227C +S11346D01A707B68D0225A6046E0FB685B083A682F +S11346E09A420AD37B6803221A703B68234AA2FBCE +S11346F00323DA087B685A6036E0FB689B083A6853 +S11347009A420AD37B6800221A703B681B4AA2FBB8 +S113471003239A087B685A6026E0FB68DB083A6842 +S11347209A420AD37B6801221A703B68134AA2FB9F +S113473003235A087B685A6016E0FB681B093A6831 +S11347409A420BD97B6802221A703B685B000B4AC1 +S1134750A2FB03235A087B685A6005E07B680222A7 +S11347601A707B6868225A60002318461037BD46C9 +S113477080BD00BFA87C0000C07C0000ABAAAAAA30 +S113478080B400AF0A4B1B6A03F01003002B0AD15C +S1134790074B14221A6200BF054B1B6A03F08043C7 +S11347A0B3F1804FF8D100BFBD465DF8047B70477C +S11347B00040C84080B588B000AF0346FB710023B9 +S11347C0FB6197F90730052B59D801A252F823F061 +S11347D0E9470000254800002B4800006548000018 +S11347E06B480000754800002C4BD3F80032DBB254 +S11347F0FB602A4BD3F80032DB0B03F00303BB60EE +S1134800BB680133012202FA03F3BB60BB685B009F +S1134810234AB2FBF3F3FB61FB69FA6802FB03F37F +S1134820FB612FE01F4BFB612CE01C4BD3F8903253 +S113483003F07F03BB61194BD3F8803223F040436C +S11348407B61164BD3F8A03223F040433B617A6975 +S11348503B69B2FBF3F2BB691344114A02FB03F355 +S1134860FB610FE0104BFB610CE00420FEF768FED7 +S1134870F86107E00520FEF763FEF86102E00B48EB +S1134880FEF7D4FEFB69002B02D10948FEF7CEFEE9 +S1134890FB6918462037BD4680BD00BF0040C840B4 +S11348A000366E0100CA9A3B00389C1C487D00000B +S11348B0647D000080B584B000AF0346FB71002323 +S11348C0FB60FB791E2B00F2BD8001A252F823F09D +S11348D04D490000674900006D490000734900001C +S11348E079490000794900007F4900007F490000B0 +S11348F08949000089490000934900009F4900004C +S1134900AB490000B7490000C3490000C349000097 +S1134910454A0000CD490000D9490000E54900009E +S1134920F1490000FD490000FD490000074A00006C +S1134930154A0000294A0000294A0000334A0000B1 +S1134940334A00003D4A00003D4A0000444BD3F87E +S1134950C03003F00403002B02D0424BFB6074E030 +S1134960414BFB6071E0414BFB606EE0404BFB60F0 +S11349706BE0404BFB6068E03D4BFB6065E0002072 +S1134980FFF718FFF86060E00220FFF713FFF860FC +S11349905BE000210220FFF78BFDF86055E0012168 +S11349A00220FFF785FDF8604FE002210220FFF7A7 +S11349B07FFDF86049E003210220FFF779FDF860EC +S11349C043E00320FFF7F6FEF8603EE000210320F9 +S11349D0FFF76EFDF86038E001210320FFF768FD62 +S11349E0F86032E002210320FFF762FDF8602CE05A +S11349F003210320FFF75CFDF86026E00120FFF7A8 +S1134A00D9FEF86021E00120FFF7D4FE03465B08DD +S1134A10FB601AE00120FFF7CDFE0346164AA2FB15 +S1134A2003239B08FB6010E00420FFF7C3FEF8603B +S1134A300BE00520FFF7BEFEF86006E002F0A3FAE3 +S1134A40F86002E00D48FEF7F1FDFB68002B02D18F +S1134A500B48FEF7EBFDFB6818461037BD4680BDDA +S1134A600040C8400024F40000093D00006CDC0252 +S1134A7000366E010084D717CDCCCCCC7C7D0000F1 +S1134A80987D0000F0B585B000AF786039607B6830 +S1134A90002B02D11E48FEF7C9FD7B6818464FF073 +S1134AA000013B681A464FF0000302FB01FC00FBC7 +S1134AB003F66644A0FB020173181946154A4FF029 +S1134AC0000302F0D0FD02460B46C7E90223D7E9F2 +S1134AD00223012B08BF002A02D30F48FEF7A6FDCC +S1134AE0D7E902014FF003024FF0000302F0BBFDCF +S1134AF002460B46941843EB0305C7E90245BB681D +S1134B00184602F061FA00BF1437BD46F0BD00BF7D +S1134B10B47D000040420F00D87D000080B483B013 +S1134B2000AF03460A46FB711346BB71BB791946B5 +S1134B30114AFB7903F540735B0113441B684B4036 +S1134B4003F00103002B10D0BB791A460A49FB7904 +S1134B5002F0010203F540735B010B441A60BFF3DA +S1134B604F8F00BFBFF36F8F00BF00BF0C37BD4630 +S1134B705DF8047B704700BF0000CC4080B584B072 +S1134B8000AF78600023FB6009E00D4AFB6852F82F +S1134B9023307A689A4206D0FB680133FB60FB68D5 +S1134BA00F2BF2D900E000BFFB680F2B02D9054898 +S1134BB0FEF73CFDFB6818461037BD4680BD00BFBC +S1134BC0007E0000507E000080B586B000AFF86023 +S1134BD0B9607A60F868FFF7D1FF78617B690D2BC3 +S1134BE00CD8204A7B6913441B788A2B06D01D4AB3 +S1134BF07B6913441B78184602F0F6F9FB685A697E +S1134C000121BB6801FA03F3DB431A40FB685A61D4 +S1134C107B681B78002B0AD1FB685A680121BB68AA +S1134C2001FA03F3DB431A40FB685A600FE07B6828 +S1134C305B781A46B968F86800F016F8FB685A6899 +S1134C400121BB6801FA03F31A43FB685A607B68CD +S1134C509B781A46B968F86802F0D4F900BF18378F +S1134C60BD4680BD407E000080B584B000AFF860D2 +S1134C70B9601346FB71BB681F2B02D90E48FEF7BF +S1134C80D5FCFB79002B0AD1FB681A680121BB68AB +S1134C9001FA03F3DB431A40FB681A6008E0FB687F +S1134CA01A680121BB6801FA03F31A43FB681A600E +S1134CB000BF1037BD4680BD847E000080B487B03D +S1134CC000AFF860B9601346FB71BB683B61FB68D9 +S1134CD0DA690121BB6801FA03F3DB431A40FB687C +S1134CE0DA61BB680F2B03D8FB680C337B6105E0EA +S1134CF0FB6810337B613B69103B3B61FB79013BF3 +S1134D00042B52D801A252F823F000BF214D000019 +S1134D10394D00005D4D0000814D0000974D0000AD +S1134D207B691A683B695B00032101FA03F3DB43E7 +S1134D301A407B691A6039E07B691A683B695B0039 +S1134D40032101FA03F3DB431A403B695B000121B1 +S1134D5001FA03F31A437B691A6027E07B691A6836 +S1134D603B695B00032101FA03F3DB431A403B690F +S1134D705B00022101FA03F31A437B691A6015E010 +S1134D807B691A683B695B00032101FA03F31A4348 +S1134D907B691A600AE0FB68DA690121BB6801FAE1 +S1134DA003F31A43FB68DA6100E000BF00BF1C375D +S1134DB0BD465DF8047B704780B483B000AF034602 +S1134DC00A46FB711346BB71BB791946114AFB793C +S1134DD003F540735B0113441B684B4003F001036C +S1134DE0002B10D0BB791A460A49FB7902F0010264 +S1134DF003F540735B010B441A60BFF34F8F00BF90 +S1134E00BFF36F8F00BF00BF0C37BD465DF8047B56 +S1134E10704700BF0000CC4080B584B000AF78601C +S1134E200023FB6009E00D4AFB6852F823307A68DE +S1134E309A4206D0FB680133FB60FB680C2BF2D965 +S1134E4000E000BFFB680C2B02D90548FEF7EEFB1F +S1134E50FB6818461037BD4680BD00BFA07E000029 +S1134E60E47E000080B58CB000AFF860B9607A6071 +S1134E70BB68002B02D1A248FEF7D8FBBB681B68B5 +S1134E80002B02D19F48FEF7D1FBBB681B7A042B91 +S1134E9002D99D48FEF7CAFBBB685B7A042B02D992 +S1134EA09A48FEF7C3FB0023FB62BB681B68FB61E7 +S1134EB0002387F823300023FB84042387F822305F +S1134EC041E07A6813469B0013445B001946BB68B3 +S1134ED01B6897F8222002FB03F3B1FBF3F30533BD +S1134EE08B4AA2FB0323DB08BB84BB8C002B01D1C0 +S1134EF00123BB8497F82230BA8C02FB03F37A684F +S1134F00B2FBF3F37B61BB681B687A699A4204D9EC +S1134F10BB681B687A69D31A03E0BB681A687B69AB +S1134F20D31A3B613A69FB699A4207D83B69FB6132 +S1134F3097F8223087F82330BB8CFB8497F8223013 +S1134F40013387F8223097F82230202BB9D9BB6877 +S1134F501B68704AA2FB03235A0913465B001344DF +S1134F60FA699A4203D940F22153FB62FCE0F868E3 +S1134F70FFF752FFB861684ABB6913441B781846AF +S1134F8002F051F8F86802F05CF8FB681B69BB6238 +S1134F9097F82330032B07D997F82330072B03D82E +S1134FA0BB6A43F40033BB62BB6A23F0F853BB62B1 +S1134FB097F82330013B1B0603F0F853BA6A1343F6 +S1134FC0BB62BA6A554B1340BB62FB8CC3F30C0241 +S1134FD0BB6A1A43FB681A61FB681B6923F0005221 +S1134FE0FB681A61FB689A694D4B1340BB62BB684E +S1134FF01B791A46BB68DB7B1B0203F4E0631A438C +S1135000BB689B7B9B0003F004031343BA6A1343FE +S1135010BB62BB685B79012B0DD1BB681B79002B8C +S113502004D0BB6A23F40063BB620CE0BB6A43F4A4 +S11350300063BB6207E0BB681B79002B03D0BB6A2B +S113504043F01003BB62FB68BA6A9A61FB681B6990 +S113505023F40053BB62BB68DB795B0303F40052A7 +S1135060BB6A1A43FB681A61BB685B7A1B04BA68A3 +S1135070127A1A43FB68DA62FB689B6A43F088027F +S1135080FB689A62FB689B6A43F44042FB689A623D +S1135090244BBB62BB6A43F00043BB62BB6A43F46C +S11350A04043BB62FB685A6ABB685B7B1B0103F02D +S11350B01001BB681B7B5B0103F020030B431A4305 +S11350C0FB685A62BB689B7A002B05D0FB685B6A5D +S11350D043F00802FB685A62BB68DB7A002B05D0F8 +S11350E0FB685B6A43F00102FB685A62BB689B7908 +S11350F0002B19D0BB6A43F00053BB6218E000BF19 +S1135100207F0000447F0000747F0000D07F0000F7 +S1135110CDCCCCCC1F85EB51D47E000000E0FFFF4A +S1135120E8F8FFFF00001F40BB6A23F00053BB6296 +S1135130FB685A69BB6A1A43FB685A61FB689B693E +S1135140BB62BB681B7C002B03D0BB6A43F4002307 +S1135150BB62BB685B7C002B03D0BB6A43F4802337 +S1135160BB62FB68BA6A9A61FB6A18463037BD466F +S113517080BD00BF80B582B000AF78607B68002B33 +S113518002D11D48FEF752FA14220021786802F079 +S113519038F97B684FF4E1321A607B6800221A7197 +S11351A07B6800225A717B6800229A717B68002216 +S11351B0DA717B6800221A727B6800225A727B685B +S11351C000229A727B680022DA727B6800225A738A +S11351D07B6800221A737B6800229A737B68002222 +S11351E0DA737B6800221A747B6800225A7400BF49 +S11351F00837BD4680BD00BF2C80000080B586B056 +S113520000AFF860B9607A60BB68002B02D1144823 +S1135210FEF70CFABB687B617B683B6111E000BF61 +S1135220FB685B6903F40003002BF9D07B691B78EE +S11352301A46FB68DA617B6901337B613B69013B98 +S11352403B613B69002BEAD100BFFB685B6903F457 +S11352508003002BF9D0002318461837BD4680BDC3 +S11352605080000080B500AF054B1B68054A5268AA +S1135270114605489847BFF34F8F00BF00BF80BD5C +S1135280C48800009088000000C0074080B500AFCB +S1135290054B1B68054A9268114605489847BFF3B9 +S11352A04F8F00BF00BF80BDC488000090880000FD +S11352B00000084080B500AF054B1B68054AD26862 +S11352C0114605489847BFF34F8F00BF00BF80BD0C +S11352D0C4880000908800000040084080B500AFFA +S11352E0054B1B68054A1269114605489847BFF3E8 +S11352F04F8F00BF00BF80BDC488000090880000AD +S11353000080084080B500AF054B1B68054A526910 +S1135310114605489847BFF34F8F00BF00BF80BDBB +S1135320C48800009088000000C0084080B500AF29 +S1135330054B1B68054A9269114605489847BFF317 +S11353404F8F00BF00BF80BDC4880000908800005C +S11353500000094080B500AF054B1B68054AD269BF +S1135360114605489847BFF34F8F00BF00BF80BD6B +S1135370C4880000908800000040094080B500AF58 +S1135380054B1B68054A126A114605489847BFF346 +S11353904F8F00BF00BF80BDC4880000908800000C +S11353A00080094080B500AF054B1B68054A526A6E +S11353B0114605489847BFF34F8F00BF00BF80BD1B +S11353C0C48800009088000000C0094080B500AF88 +S11353D0054B1B68054A926A114605489847BFF376 +S11353E04F8F00BF00BF80BDC488000090880000BC +S11353F000000A4080B500AF054B1B68054AD26A1D +S1135400114605489847BFF34F8F00BF00BF80BDCA +S1135410C4880000908800000040C24080B584B079 +S113542000AF78600B46FB70FB78002B22D17B68C1 +S1135430D3F8103523F005027B68C3F810252D49F5 +S11354404FF47A70FFF71EFB7B68D3F8103523F412 +S1135450C1527B68C3F8102526494FF47A70FFF7D0 +S113546011FB7B68D3F8103523F400227B68C3F862 +S113547010253BE07B68D3F8103523F001027B68EC +S1135480C3F810251B494FF47A70FFF7FBFA7B68C9 +S1135490D3F8103543F400227B68C3F8102515496E +S11354A04FF47A70FFF7EEFA7B68D3F81035FB609F +S11354B0FB6823F4C053FB60FB78DB0203F4C052A7 +S11354C0FB68134343F02003FB607B68FA68C3F86E +S11354D0102508494FF47A70FFF7D4FA7B68D3F8A3 +S11354E0103543F004027B68C3F8102500BF103761 +S11354F0BD4680BD00CA9A3B80B582B000AF7860DB +S11355000B46FB70FB78002B2FD07B68D3F830352B +S113551043F001027B68C3F8302527494FF47A70C1 +S1135520FFF7B0FA7B68D3F8303543F400327B6878 +S1135530C3F8302520494FF47A70FFF7A3FA7B684B +S1135540D3F8303543F480227B68C3F830251A49F8 +S11355504FF47A70FFF796FA7B68D3F8303523F06E +S113556004027B68C3F8302521E07B68D3F830352A +S113557043F005027B68C3F830250F494FF47A7075 +S1135580FFF780FA7B68D3F8303523F480227B68F8 +S1135590C3F8302508494FF47A70FFF773FA7B6833 +S11355A0D3F8303523F400327B68C3F8302500BFCC +S11355B00837BD4680BD00BF00CA9A3B80B582B0A3 +S11355C000AF78607B68002B02D10A48FEF72EF802 +S11355D003220021786801F014FF7B6801221A700D +S11355E07B6803225A707B680F229A7000BF0837C9 +S11355F0BD4680BD7480000080B584B000AF786083 +S113560039603B68002B02D13448FEF70FF87B6801 +S1135610D3F8503523F070427B68C3F850257B687B +S1135620D3F850253B681B781B071A437B68C3F8E3 +S113563050257B68D3F8503523F070627B68C3F83B +S113564050257B68D3F850253B681B781B0603F074 +S113565070631A437B68C3F850257B68D3F85035D0 +S1135660FB60FA681E4B1340FB603B685B789B0051 +S113567003F01C023B689B785B0103F4F073134353 +S113568043F00203BB60BB68C3F30C03FA68134323 +S1135690FB607B68FA68C3F850257B68D3F86035F3 +S11356A023F4E0527B68C3F860257B68D3F8603547 +S11356B043F480627B68C3F860257B68D3F8603567 +S11356C043F080727B68C3F8602506496420FFF7C5 +S11356D0D9F900BF1037BD4680BD00BF94800000DB +S11356E000E0FFFF00CA9A3B80B584B000AF814B55 +S11356F0D3F888307F4A43F47003C2F888307D4B76 +S11357007D4A9A607D4B1B889BB203F00403002BF7 +S113571007D07A4B1B889BB2784A23F004039BB2D0 +S11357201380774B1B889BB203F00403002B07D034 +S1135730734B1B889BB2724A23F004039BB2138001 +S1135740704B1B6803F40053002B03D06D4B6E4A5F +S11357505A6007E06B4B4CF220525A60694B4DF68D +S113576028125A60674B4FF6FF729A60654B1B68AC +S113577023F0A003634A43F020031360634B1B68C8 +S113578003F40053002B03D0604B5F4A5A6007E0D8 +S11357905E4B4CF220525A605C4B4DF628125A6014 +S11357A05A4B4FF6FF729A60584B1B6823F0A003C4 +S11357B0564A43F020031360554B1B6803F0010362 +S11357C0002B05D0524B1B68514A23F00103136090 +S11357D0484B5B6903F40033B3F5003F22D0454BDB +S11357E05B6903F40033002B1BD1BFF34F8F00BF61 +S11357F0BFF36F8F00BF3F4B0022C3F85022BFF3AB +S11358004F8F00BFBFF36F8F00BF3A4B5B69394ABC +S113581043F400335361BFF34F8F00BFBFF36F8F67 +S113582000E000BF334B5B6903F48033B3F5803F82 +S11358303FD0304B5B6903F48033002B38D12D4BC0 +S11358400022C3F88420BFF34F8F00BF294BD3F845 +S11358508030FB60FB685B0BC3F30E03BB60FB682B +S1135860DB08C3F309037B60BB685A0143F6E073AA +S113587013407A6892071F491343C1F860327B686A +S11358805A1E7A60002BEFD1BB685A1EBA60002BF7 +S1135890E5D1BFF34F8F00BF164B5B69154A43F444 +S11358A080335361BFF34F8F00BFBFF36F8F00E0AE +S11358B000BF184B1B6F174A23F400531367164B92 +S11358C0154A1B69D363144B4FF0FF321A61094B1D +S11358D05B69084A43F010035361BFF34F8F00BF65 +S11358E0BFF36F8F00BF01F0C9FB00BF1037BD4687 +S11358F080BD00BF00ED00E0003000000000034068 +S1135900004003400080034020C528D90000C14066 +S113591010E000E000400E400040C04080B58AB076 +S113592000AF786039600023FB847B68002B02D1D0 +S11359302848FDF77BFE3B68002B02D12648FDF783 +S113594075FE3B681B7B0C2B02D92448FDF76EFEC9 +S11359503B681B7B1A46224B53F82230002B02D1A2 +S11359602048FDF763FE07F108031846FFF702FC21 +S11359703B689B7A002B14BF01230023DBB27B76A8 +S11359803B68DB7A002B14BF01230023DBB23B7698 +S11359903B681B7B1A46124B53F822003B681A687B +S11359A007F108031946FFF75DFA38623B6A002BDA +S11359B005D0386A01F069FB0346FB8405E07B6887 +S11359C0FB613B681A7BFB691A70FB8C184628370D +S11359D0BD4680BDE88000000C81000030810000DD +S11359E0B48000009881000080B586B000AFF860F4 +S11359F0B9607A60FB68002B02D11048FDF716FEEF +S1135A00BB68002B02D10E48FDF710FE7B68002B0B +S1135A1002D10C48FDF70AFEFB687B617B691B78A9 +S1135A201A46094B53F822307A68B9681846FFF7CA +S1135A30E5FB002318461837BD4680BDDC81000015 +S1135A400082000024820000B480000080B483B08F +S1135A5000AF0346FB71074AFB79DB0113441B6863 +S1135A601B0A03F0070318460C37BD465DF8047B98 +S1135A70704700BF0000CC4080B483B000AF034641 +S1135A80FB71064AFB79DB0113441B68DBB201336B +S1135A9018460C37BD465DF8047B70470000CC40C7 +S1135AA090B5ADF5237D00AF0246FB1D1A7007F1DA +S1135AB008031A4A184611464FF41E731A4601F099 +S1135AC09EFCFB1D1B781846FFF7C0FFC7F8840235 +S1135AD0FB1D1B7807F10802DB001A44D7F8843257 +S1135AE013441B781846FEF7E5FE0446FB1D1B789D +S1135AF01846FFF7C1FF0346B4FBF3F3C7F880323F +S1135B00D7F88032002B02D10548FDF78FFDD7F876 +S1135B108032184607F52377BD4690BDDC8200002D +S1135B204882000080B483B000AF0346FB71074A8B +S1135B30FB79DB0113441B681B0A03F007031846B7 +S1135B400C37BD465DF8047B704700BF0000CC40B5 +S1135B5080B483B000AF0346FB71064AFB79DB01D6 +S1135B6013441B68DBB2013318460C37BD465DF89D +S1135B70047B70470000CC4080B582B000AF034680 +S1135B803960FB713B68002B02D11A48FDF74EFDCA +S1135B903B685B781B0503F470023B689B781B042D +S1135BA003F470231A433B68DB781B0203F4E063BD +S1135BB01A433B681B79013BDBB21A433B681B78F1 +S1135BC0002B05D03B681B781B0603F0807300E0B4 +S1135BD000230948F9791A43CB0103441A60BFF33F +S1135BE04F8F00BFBFF36F8F00BF00BF0837BD46A4 +S1135BF080BD00BF548500000000CC4090B5ADF5D9 +S1135C00237D00AF0246FB1D1A7007F108031A4AF0 +S1135C10184611464FF41E731A4601F0F0FBFB1DA3 +S1135C201B781846FFF77EFFC7F88402FB1D1B781C +S1135C3007F10802DB001A44D7F8843213441B78B6 +S1135C401846FEF737FE0446FB1D1B781846FFF77F +S1135C507FFF0346B4FBF3F3C7F88032D7F88032F2 +S1135C60002B02D10548FDF7E1FCD7F88032184635 +S1135C7007F52377BD4690BDE88500004882000003 +S1135C8080B58AB000AF07F1200300221A601A71B0 +S1135C901621724801F058FB07F11C031846FFF760 +S1135CA08DFC07F11C0319466D48FFF7A5FC012183 +S1135CB06B48FFF7B3FB01216948FFF71DFC07F1AF +S1135CC0080300221A605A609A60DA601A6101239C +S1135CD03B7216233B6103237B60FEF751FD0023D7 +S1135CE087F82330012387F8243007F1200319466D +S1135CF00020FFF741FF07F1200319460820FFF7B2 +S1135D003BFF4FF47A7001F046F9042387F82330FF +S1135D10012387F8243007F1200319460020FFF7F8 +S1135D202BFF002387F82330F02387F8243007F172 +S1135D30200319460820FFF71FFF07F10803184640 +S1135D40FEF77AFA3B1D1846FEF71AFC062387F87D +S1135D502330162387F8243007F12003194619202D +S1135D60FFF70AFF102201210220FEF71BFB062386 +S1135D7087F82330032387F8243007F120031946DA +S1135D800420FFF7F9FE042387F82330022387F861 +S1135D90243007F1200319460220FFF7EDFE00230B +S1135DA087F82330012387F8243007F120031946AC +S1135DB02520FFF7E1FE002387F82330012387F82D +S1135DC0243007F1200319462920FFF7D5FE0023CC +S1135DD087F82330012387F8243007F1200319467C +S1135DE00E20FFF7C9FE002387F82330012387F82C +S1135DF0243007F1200319460F20FFF7BDFE0023CE +S1135E0087F82330012387F8243007F1200319464B +S1135E102B20FFF7B1FE002387F82330012387F8F6 +S1135E20243007F1200319460D20FFF7A5FE0023B7 +S1135E3087F82330012387F8243007F1200319461B +S1135E403120FFF799FE0020FFF7D8FE0346054AEC +S1135E50136000BF2837BD4680BD00BF0080CA4024 +S1135E600040C8406888000080B483B000AF034697 +S1135E700A46FB711346BB71BB791946114AFB797B +S1135E8003F540735B0113441B684B4003F00103AB +S1135E90002B10D0BB791A460A49FB7902F00102A3 +S1135EA003F540735B010B441A60BFF34F8F00BFCF +S1135EB0BFF36F8F00BF00BF0C37BD465DF8047B96 +S1135EC0704700BF0000CC4080B584B002AF3120E1 +S1135ED001F063FA01233B7100237B710023BB7142 +S1135EE03B1D1A4603210848FEF76EFE002301936A +S1135EF0064B0093002300220A21054801F05BFAB7 +S1135F0000BF0837BD4680BD0040C64060830E40D8 +S1135F101C810E4010B5174A0020174901F0F0FA11 +S1135F204FF480734FF480620021124801F083FA29 +S1135F30124A1349012001F0E3FA4FF480734FF43D +S1135F40806200210D4801F076FA0E4B1B68022B8B +S1135F500EDD0D4A02200A4901F0D2FA4FF4807393 +S1135F604FF4806200210848BDE8104001F063BA94 +S1135F7010BD00BFCC8800006488000008890000C0 +S1135F806688000060880000448900002DE9F04123 +S1135F90D56880460E461446EB02576905D545F090 +S1135FA01005384601F050FCA061124B2B408BB118 +S1135FB0A169384601F051FC002806DAE3684FF085 +S1135FC0FF3043F08003E36011E025F4003525F051 +S1135FD01005E56032464146384601F044FC20F0A5 +S1135FE00041761AA1690E44A6610028E6D1BDE8F5 +S1135FF0F08100BF10000200F7B5044698B103B069 +S1136000BDE8F04001F00FBB07FB0460013401F070 +S11360100AFB002818BF4FF0FF35019BA342F3DCB5 +S1136020284603B0F0BD034B05463C27024E1B68CF +S11360300193F2E760880000CC8800002DE9F84362 +S1136040C66804463C4B0D46904633401BB90227B4 +S11360503846BDE8F883D0F81490484601F0F7FBC1 +S113606007460028F3D1B8F1010F2CD0B8F1020F84 +S11360702ED0B8F1000FEAD1002DE8DBB00404D52E +S11360802368E26A9A4238BFE362A169A9420EDC3E +S11360902368E26A934238BF134622695818801A6B +S11360A0A84204DB206B01EB000CAC4534DC00237C +S11360B046F02006A562C4E9013326F4032626F03F +S11360C04006E660C4E7204600F038F80544D3E70C +S11360D0484601F0B9FBB0F1000CE26804DA42F082 +S11360E080020127E260B3E723681846A3691946D2 +S11360F0E36A98422CBF0918C91892060B46216915 +S1136100A3EB010303D5A26A9342B8BF13466345C8 +S1136110ACBFED186544AFE76D1AB10744BF281A48 +S1136120A060F10726F0200644BF9B1AEB1A154421 +S113613048BF63602560C0E703001000C3689A0786 +S113614005D10E4B21224FF0FF301A60704703F047 +S113615020021B0309D512B1806A01387047036815 +S1136160826900691344181AF7E70AB1806A704714 +S11361700368826900691344181A7047C8880000CC +S113618080B586B000AFF860B9603B601346FB7120 +S113619000237B6100237B611CE0BB681B68013327 +S11361A07F2B08D9BB681B681946F868FDF74AFAC3 +S11361B0BB6800221A60BB681B681A46FB6813445C +S11361C0FA791A70BB681B685A1CBB681A607B6931 +S11361D001337B617A693B689A42DEDB00BF00BF12 +S11361E01837BD4680BD80B485B000AF7860396093 +S11361F00023FB600023FB7220E07B681B685A1CB1 +S11362007B681A607B681B681B78BB72BB7A2F2B78 +S11362100DD9BB7A392B0AD8FA6813469B0013446C +S11362205B001A46BB7A1344303BFB6006E07B6894 +S11362301B685A1E7B681A600123FB72FB7A002BD1 +S1136240DBD0FB6818461437BD465DF8047B704705 +S113625080B489B000AFF860B9607A60FB681B68ED +S1136260FB610623BB610023FB75FB690133FB6102 +S1136270FB691B782E2B24D10023BB610023FB7503 +S11362801BE0FB690133FB61FB691B78BB75BB7DBC +S11362902F2B0DD9BB7D392B0AD8BA6913469B0025 +S11362A013445B001A46BB7D1344303BBB6104E0DE +S11362B0FB69013BFB610123FB75FB7D002BE0D0F7 +S11362C002E0FB69013BFB61FB68FA691A60BB6988 +S11362D018462437BD465DF8047B704780B485B00A +S11362E000AF0346FB710023FB60FB796F2B08D0E2 +S11362F0FB79622B05D0FB79702B02D0FB79752BCF +S113630001D10123FB60FB6818461437BD465DF8D4 +S1136310047B704780B485B000AF0346FB71002353 +S1136320FB60FB79642B02D0FB79692B01D101233B +S1136330FB60FB6818461437BD465DF8047B704764 +S113634090B585B000AFF860B9607A60FB70BA6848 +S11363507B68D31A7C6A2022F96AB86AA04708E0ED +S11363603B6A5A1E3A621A787C6A0123F96AB86A4F +S1136370A0473B6A1B78002BF2D100BF00BF143743 +S1136380BD4690BD90B585B000AFF860B9607A6045 +S1136390FB70BA687B68D31A7C6A2022F96AB86AEF +S11363A0A04708E03B6A5A1E3A621A787C6A0123C5 +S11363B0F96AB86AA0473B6A1B78002BF2D100BF88 +S11363C000BF1437BD4690BD80B485B000AF03460E +S11363D0FB710023FB60FB79662B02D0FB79462B13 +S11363E001D10123FB60FB6818461437BD465DF8F4 +S11363F0047B704780B485B000AF0346FB71002373 +S1136400FB60FB79782B02D0FB79582B01D1012357 +S1136410FB60FB6818461437BD465DF8047B704783 +S113642080B485B000AF78607B681B68FB60FB6854 +S11364300133FB60FB681B78FB72FB7A682BF6D098 +S1136440FB7A6C2BF3D0FB68013BFB607B68FA683A +S11364501A6000BF1437BD465DF8047B704780B4F2 +S113646085B000AF0346FB71FB796F2B02D1082383 +S1136470FB730DE0FB79622B02D10223FB7307E06F +S1136480FB79702B02D11023FB7301E00A23FB7309 +S1136490FB7B18461437BD465DF8047B704780B417 +S11364A08DB000AFF860B9607A603B600023FB6197 +S11364B0FB68BB61BB695A1CBA6100221A707B6815 +S11364C0002B33D0BB681B68FB62FB6A002B29D10D +S11364D0BB6930221A70FB690133FB61FB6960E020 +S11364E0FA6A3B6892FBF3F33B613B693A6802FB4F +S11364F003F3FA6AD31ABB62BB6A002B04DABB6AE1 +S1136500C3F13003BB6202E0BB6A3033BB623B6958 +S1136510FB62BB695A1CBA61BA6AD2B21A70FB69CF +S11365200133FB61FB6A002BDAD139E0BB681B68DD +S11365307B627B6A002B30D1BB6930221A70FB6905 +S11365400133FB61FB692CE03B687A6AB2FBF3F32D +S11365507B613B687A6902FB03F37A6AD31A3B6274 +S11365603B6A092B03D83B6A30333B620AE097F855 +S11365703830002B01D0412200E061223B6A1344F1 +S11365800A3B3B627B697B62BB695A1CBA613A6A0B +S1136590D2B21A70FB690133FB617B6A002BD3D141 +S11365A0FB6918463437BD465DF8047B704790B5E7 +S11365B09FB004AFF860B9607A603B600023FB656C +S11365C00023BB650023FB61002387F857300023B9 +S11365D07B61FB687B647B6C1B78002B00F029815A +S11365E07B6C1B7887F8563097F85630252B0BD0E8 +S11365F097F8562007F11C013C6801237868A047EE +S11366007B6C01337B6413E1012387F8633007F16A +S1136610080207F1440311461846FFF7E4FD386504 +S113662007F1080107F1440300221846FFF710FEA2 +S1136630F86407F144031846FFF7F2FE7B6C01335C +S11366407B647B6C1B7887F8563097F85630184675 +S1136650FFF760FE0346012B25D1BB681A1DBA6003 +S11366601B68BB6107F1180107F1200097F863303C +S113667000930A230122FFF712FFB86507F12002F5 +S1136680BB6D1344FB65BA6D07F11C0303937B6870 +S113669002933B680193FB6D00930023396D002046 +S11366A0FFF74EFEC1E097F856301846FFF78CFE10 +S11366B00346012B06D1BB68073323F007030833D5 +S11366C0BB60B2E097F856301846FFF793FE0346D6 +S11366D0012B2ED197F85630782B02D1002387F85E +S11366E06330BB681A1DBA601B687B6107F1140133 +S11366F007F1200097F86330009310230022FFF77E +S1136700CEFEB86507F12002BB6D1344FB65BA6D7C +S113671097F8631007F11C0303937B6802933B68AB +S11367200193FB6D00930B46396D0020FFF72AFEA1 +S11367307BE097F856301846FFF7D0FD0346012B4F +S11367402FD1BB681A1DBA601B687B6197F856305D +S11367501846FFF784FE034687F8573097F857200A +S113676007F1140107F1200097F8633000931346F2 +S11367700022FFF794FEB86507F12002BB6D1344B5 +S1136780FB65BA6D07F11C0303937B6802933B68B6 +S11367900193FB6D00930023396D0020FFF7D0FDBA +S11367A043E097F85630632B0DD1BB681A1DBA60CD +S11367B01B68BB64BB6CDAB207F11C013C680123A3 +S11367C07868A04731E097F85630732B25D1BB6821 +S11367D01A1DBA601B687B667B6E002B25D0786E11 +S11367E000F048FE0346BB653A6DBB6DD31A07F152 +S11367F01C013C6820227868A04709E07B6E5A1C83 +S11368007A661A7807F11C013C6801237868A0476E +S11368107B6E1B78002BF1D107E097F8562007F127 +S11368201C013C6801237868A0477B6C01337B64BE +S1136830D1E600BFFB6918466C37BD4690BD08B56C +S1136840FCF77DFE08BD08B5FCF779FE08BD08B568 +S1136850FCF775FE08BD08B5FCF771FE08BD08B568 +S1136860FCF76DFE08BD08B5FCF769FE08BD08B568 +S1136870FCF765FE08BD08B5FCF761FE08BD08B568 +S1136880FCF75DFE08BD08B5FCF759FE08BD08B568 +S1136890FCF755FE08BD08B5FCF751FE08BD08B568 +S11368A0FCF74DFE08BD08B5FCF749FE08BD08B568 +S11368B0FCF745FE08BD08B5FCF741FE08BD08B568 +S11368C0FCF73DFE08BD08B5FCF739FE08BD08B568 +S11368D0FCF735FE08BD08B5FCF731FE08BD08B568 +S11368E0FEF7C0FC08BD08B5FEF7D0FC08BD08B52E +S11368F0FEF7E0FC08BD08B5FEF7F0FC08BD08B5DE +S1136900FEF700FD08BD08B5FEF710FD08BD08B58B +S1136910FEF720FD08BD08B5FEF730FD08BD08B53B +S1136920FEF740FD08BD08B5FEF750FD08BD08B5EB +S1136930FEF760FD08BD08B5FCF701FE08BD08B50B +S1136940FCF7FDFD08BD08B5FCF7F9FD08BD08B569 +S1136950FCF7F5FD08BD08B5FCF7F1FD08BD08B569 +S1136960FCF7EDFD08BD08B5FCF7E9FD08BD08B569 +S1136970FCF7E5FD08BD08B5FCF7E1FD08BD08B569 +S1136980FCF7DDFD08BD08B5FCF7D9FD08BD08B569 +S1136990FCF7D5FD08BD08B5FCF7D1FD08BD08B569 +S11369A0FCF7CDFD08BD08B5FCF7C9FD08BD08B569 +S11369B0FCF7C5FD08BD08B5FCF7C1FD08BD08B569 +S11369C0FCF7BDFD08BD08B5FCF7B9FD08BD08B569 +S11369D0FCF7B5FD08BD08B5FCF7B1FD08BD08B569 +S11369E0FCF7ADFD08BD08B5FCF7A9FD08BD08B569 +S11369F0FCF7A5FD08BD08B5FCF7A1FD08BD08B569 +S1136A00FCF79DFD08BD08B5FCF799FD08BD08B568 +S1136A10FCF795FD08BD08B5FCF791FD08BD08B568 +S1136A20FCF78DFD08BD08B5FCF789FD08BD08B568 +S1136A30FCF785FD08BD08B5FCF781FD08BD08B568 +S1136A40FCF77DFD08BD08B5FCF779FD08BD08B568 +S1136A50FCF775FD08BD08B5FCF771FD08BD08B568 +S1136A60FCF76DFD08BD08B5FCF769FD08BD08B568 +S1136A70FCF765FD08BD08B5FCF761FD08BD08B568 +S1136A80FCF75DFD08BD08B5FCF759FD08BD08B568 +S1136A90FCF755FD08BD08B5FCF751FD08BD08B568 +S1136AA0FCF74DFD08BD08B5FCF749FD08BD08B568 +S1136AB0FCF745FD08BD08B5FCF741FD08BD08B568 +S1136AC0FCF73DFD08BD08B5FCF739FD08BD08B568 +S1136AD0FCF735FD08BD08B5FCF731FD08BD08B568 +S1136AE0FCF72DFD08BD08B5FCF729FD08BD08B568 +S1136AF0FCF725FD08BD08B5FCF721FD08BD08B568 +S1136B00FCF71DFD08BD08B5FCF719FD08BD08B567 +S1136B10FCF715FD08BD08B5FCF711FD08BD08B567 +S1136B20FCF70DFD08BD08B5FCF709FD08BD08B567 +S1136B30FCF705FD08BD08B5FCF701FD08BD08B567 +S1136B40FCF7FDFC08BD08B5FCF7F9FC08BD08B569 +S1136B50FCF7F5FC08BD08B5FCF7F1FC08BD08B569 +S1136B60FCF7EDFC08BD08B5FCF7E9FC08BD08B569 +S1136B70FCF7E5FC08BD08B5FCF7E1FC08BD08B569 +S1136B80FCF7DDFC08BD08B5FCF7D9FC08BD08B569 +S1136B90FCF7D5FC08BD08B5FCF7D1FC08BD08B569 +S1136BA0FCF7CDFC08BD08B5FCF7C9FC08BD08B569 +S1136BB0FCF7C5FC08BD08B5FCF7C1FC08BD08B569 +S1136BC0FCF7BDFC08BD08B5FCF7B9FC08BD08B569 +S1136BD0FCF7B5FC08BD08B5FCF7B1FC08BD08B569 +S1136BE0FCF7ADFC08BD08B5FCF7A9FC08BD08B569 +S1136BF0FCF7A5FC08BD08B5FCF7A1FC08BD08B569 +S1136C00FCF79DFC08BD08B5FCF799FC08BD08B568 +S1136C10FCF795FC08BD08B5FCF791FC08BD08B568 +S1136C20FCF78DFC08BD08B5FCF789FC08BD08B568 +S1136C30FCF785FC08BD08B5FCF781FC08BD08B568 +S1136C40FCF77DFC08BD08B5FCF779FC08BD08B568 +S1136C50FCF775FC08BD08B5FCF771FC08BD08B568 +S1136C60FCF76DFC08BD08B5FCF769FC08BD08B568 +S1136C70FCF765FC08BD08B5FCF761FC08BD08B568 +S1136C80FCF75DFC08BD08B5FCF759FC08BD08B568 +S1136C90FCF755FC08BD08B5FCF751FC08BD08B568 +S1136CA0FCF74DFC08BD08B5FCF749FC08BD08B568 +S1136CB0FCF745FC08BD08B5FCF741FC08BD08B568 +S1136CC0FCF73DFC08BD08B5FCF739FC08BD08B568 +S1136CD0FCF735FC08BD08B5FCF731FC08BD08B568 +S1136CE0FCF72DFC08BD08B5FCF729FC08BD08B568 +S1136CF0FCF725FC08BD08B5FCF721FC08BD08B568 +S1136D00FCF71DFC08BD08B5FCF719FC08BD08B567 +S1136D10FCF715FC08BD08B5FCF711FC08BD08B567 +S1136D20FCF70DFC08BD08B5FCF709FC08BD08B567 +S1136D30FCF705FC08BD08B5FCF701FC08BD08B567 +S1136D40FCF7FDFB08BD08B5FCF7F9FB08BD08B569 +S1136D50FCF7F5FB08BD08B5FCF7F1FB08BD08B569 +S1136D60FCF7EDFB08BD08B5FCF7E9FB08BD08B569 +S1136D70FCF7E5FB08BD08B5FCF7E1FB08BD08B569 +S1136D80FCF7DDFB08BD08B5FCF7D9FB08BD08B569 +S1136D90FCF7D5FB08BD08B5FCF7D1FB08BD08B569 +S1136DA0FCF7CDFB08BD08B5FCF7C9FB08BD08B569 +S1136DB0FCF7C5FB08BD08B5FCF7C1FB08BD08B569 +S1136DC0FCF7BDFB08BD08B5FCF7B9FB08BD08B569 +S1136DD0FCF7B5FB08BD08B5FCF7B1FB08BD08B569 +S1136DE0FCF7ADFB08BD08B5FCF7A9FB08BD08B569 +S1136DF0FCF7A5FB08BD08B5FCF7A1FB08BD08B569 +S1136E00FCF79DFB08BD08B5FCF799FB08BD08B568 +S1136E10FCF795FB08BD08B5FCF791FB08BD08B568 +S1136E20FCF78DFB08BD08B5FCF789FB08BD08B568 +S1136E30FCF785FB08BD08B5FCF781FB08BD08B568 +S1136E40FCF77DFB08BD08B5FCF779FB08BD08B568 +S1136E50FCF775FB08BD08B5FCF771FB08BD08B568 +S1136E60FCF76DFB08BD08B5FCF769FB08BD08B568 +S1136E70FCF765FB08BD08B5FCF761FB08BD08B568 +S1136E80FCF75DFB08BD08B5FCF759FB08BD08B568 +S1136E90FCF755FB08BD08B5FCF751FB08BD08B568 +S1136EA0FCF74DFB08BD08B5FCF749FB08BD08B568 +S1136EB0FCF745FB08BD08B5FCF741FB08BD08B568 +S1136EC0FCF73DFB08BD08B5FCF739FB08BD08B568 +S1136ED0FCF735FB08BD08B5FCF731FB08BD08B568 +S1136EE0FCF72DFB08BD08B5FCF729FB08BD08B568 +S1136EF0FCF725FB08BD08B5FCF721FB08BD08B568 +S1136F00FCF71DFB08BD08B5FCF719FB08BD042000 +S1136F107146084202D0EFF3098001E0EFF30880E4 +S1136F2081690A884BF6AB639A4200D0FEE70231CE +S1136F30816120210160704700BF80B582B000AF3D +S1136F4003463A60FB710B46BB71BA79F8793B682A +S1136F500121FCF743FD00BF0837BD4680BD80B565 +S1136F6084B000AF03460A46FB711346BB71BA797D +S1136F70F87900230021FCF731FDF860FB6818461E +S1136F801037BD4680BD80B500AF0020FDF76AF81C +S1136F900346184680BD80B584B000AF786007F121 +S1136FA0080379681846FDF775FB0346002B06D1E4 +S1136FB007F108031846FDF797F8002300E00123C2 +S1136FC018461037BD4680BD80B483B000AF7860EA +S1136FD07B681846A0F101000028FBD100BF0C37E4 +S1136FE0BD465DF8047B704780B582B000AF0346B0 +S1136FF0FB71FB7901211846FDF790FD00BF0837AE +S1137000BD4680BD80B584B000AFF860B96013465A +S1137010FB71FB791A46B968F868FDF74FFE00BFAB +S11370201037BD4680BD80B582B000AF0346FB710A +S1137030FB7901211846FDF7BFFE00BF0837BD46A6 +S113704080BD80B483B000AF78607B689B6843F0F8 +S113705002027B689A607B689B6823F002027B686B +S11370609A6000BF0C37BD465DF8047B704780B45E +S113707000AF00BFBD465DF8047B704780B400AF2D +S113708000BFBD465DF8047B704780B485B000AF97 +S113709078607B68002B02D10023FB8102E042F67A +S11370A0AF73FB81FB8918461437BD465DF8047B3A +S11370B0704780B483B000AFEFF310833B603B684C +S11370C07B6072B600BF7B6818460C37BD465DF81E +S11370D0047B704780B485B000AF78607B68FB6048 +S11370E0FB6883F3108800BF00BF1437BD465DF80A +S11370F0047B704780B586B000AFF860B9607A60F1 +S11371003B6043F22153FB82FB681B78012B08D1BF +S1137110FB680C333A687968184600F0E0F80346D7 +S1137120FB82FB8A18461837BD4680BD80B586B001 +S113713000AFF860B9607A60FB68002BFCD0BB68D4 +S1137140002BFCD07B68002BFCD0FB687B617B6947 +S11371501B683B613B69002BFCD07B68BA6879698A +S11371603869FFF7C7FF034618461837BD4680BD88 +S113717080B584B000AF7860396043F22153FB815D +S11371803B68002BFCD07B68002BFCD000BF7B68E5 +S1137190BB6010220021B86800F033F93B681A7A0A +S11371A0BB681A703B681B7A012B0BD1BB6803F1D7 +S11371B00C023B68DB681946104600F071F8034680 +S11371C0FB8100E000BFFB8918461037BD4680BD37 +S11371D080B586B000AF786039607B68002BFCD046 +S11371E03B68002BFCD000BF7B687B613B683B6144 +S11371F0FFF75FFFF8607B699B685A1C7B699A60A4 +S1137200F868FFF767FF04220021386800F0F9F8F6 +S11372103B697A691A60002318461837BD4680BD59 +S113722080B586B000AF786039600023FB827B684C +S1137230002BFCD03B68002BFCD000BF7B683B617B +S11372403B68FB60FFF735FFB8603B695B68002B68 +S113725003D043F22253FB820AE03B69FA685A6086 +S113726004220021386800F0CCF8FB683A691A60FF +S1137270B868FFF72FFFFB8A18461837BD4680BD54 +S113728080B584B000AFF860B9607A607A68B96894 +S1137290F868FFF74BFF034618461037BD4680BD1C +S11372A080B584B000AF786039600023FB813B680F +S11372B0002BFCD07B68002BFCD000BF7B68BB603C +S11372C0BB6839681846FEF729FB0346FB81FB8936 +S11372D0002BFCD1FB8918461037BD4680BD80B514 +S11372E086B000AFF860B9607A60FB68002BFCD010 +S11372F0BB68002BFCD07B68002BFCD0FB687B6157 +S11373007B697A68B9681846FEF76EFB034618462F +S11373101837BD4680BD80B500AF1920FEF7C0FB0D +S11373200346184680BD80B582B000AFFFF7F3FF77 +S113733078607B6801224FF4E1310120FCF7AAF95F +S113734000BF0837BD4680BD80B483B000AF78600D +S11373500B46FB707B685B6923F000527B685A61C3 +S11373607B685B6823F4F852FB781B0203F4F85340 +S11373701A437B685B681A437B685A6000BF7B686A +S11373809B6803F00043B3F1004FF8D100BF00BF86 +S11373900C37BD465DF8047B704780B582B000AF02 +S11373A00346FB71FB7901211846FEF75DFD00BF22 +S11373B00837BD4680BD80B485B000AFF860B960C1 +S11373C07A603B60BB6803F00F01FB691B0103F0AB +S11373D01002FB680A431A607B68002B02D07B68AA +S11373E03A681A6000BF1437BD465DF8047B7047E5 +S11373F008B5FEF78FFDBDE80840FCF77DBA00F044 +S1137400EDB900F005BAA8B14FF0805340F80C3C38 +S113741050F8042C72B11368B3F1805F0AD150F8AC +S1137420083C516803330B4440F8083C936840F827 +S1137430043CEDE7704770B5C468A50708D0B2F501 +S1137440007F11D0B2F5806F04D0B2F5807F0BD0ED +S1137450012008E000F124010123224301610160BD +S1137460C361C260002070BD5E1E6FF07F45AE42F6 +S1137470F3D3EDE7421C10B58307044616D1234627 +S113748054F8041BA1F1013020EA010010F0803F00 +S1137490F5D011F0FF0F11D011F47F4F0CD011F47F +S11374A07F0F14BF23460333981A04E010F8013BFE +S11374B0002BE1D1801A10BD0233F5E70133F3E765 +S11374C0C368826923F0200310B5C3600446836A4D +S11374D09A420ED000F048F8E36823F4405323F0B6 +S11374E0100343F01003E360A36AA3612369E3621A +S11374F02360E36823F4804323F04003E36010BD7A +S113750070B50D4606461046144600F056F82B7822 +S1137510722B17D0772B18D0612B23D1082348F274 +S1137520020215F8011F2B2912D0622915D01B075E +S1137530E260666104D5022200212046FEF77EFD4A +S1137540204670BD00230122EBE704230222E8E772 +S113755042F0030243F00203E3E742F0040243F083 +S11375600103DEE70024EBE738B50168044605694A +S1137570C36AC06820F40022E26000F08202022A9A +S113758002D04FF0FF3038BD10F48030FBD0994268 +S113759038BF19468D4208D1E3680020E56223F420 +S11375A080332560A060E360EDE72246491B28464E +S11375B0FEF7ECFC0028EFD0E3E7F0B5C46889B02F +S11375C00546A10722D0220718D4D0E90467FEF7A4 +S11375D013FD384600F035F9230502D53046FFF790 +S11375E012FFA40DA40514F1A54F07D12022296A86 +S11375F0684600F035F9684600F00BF83C2200219B +S11376002846FFF7FEFE002009B0F0BD4FF0FF3022 +S1137610FAE710B50446FFF72DFF01462046BDE802 +S1137620104000F017B9C36870B513F003060446A0 +S113763011D09B0611D5856AFFF742FFE368204607 +S113764023F44053E360FFF78FFF002206462946E8 +S11376502046FEF7F3FC304670BD816905680D4491 +S113766001696D1AEAE75FF0000C30B51C0022D105 +S113767012006FD0914206D305460846002100F05F +S11376806FF80446284600F06BF80A462146BDE828 +S113769030401CF0404F08BF704703D54942404278 +S11376A061F100015FEA8C0C38BF70475B425242C3 +S11376B063F10003704742D41C0C04BF1B040CF19B +S11376C0100C13F07F4F04BF1B020CF1080C13F0D5 +S11376D0704F04BF1B010CF1040C13F0404F04BFA6 +S11376E00CF1020C5FEA83035CBF0CF1010C5B003C +S11376F0CCF1200E22FA0EF4234302FA0CF200FA23 +S11377000CF520FA0EF001FA0CF4204321FA0EF1E4 +S113771014461A4600F024F8A4FB0023AA1A71EBBD +S113772003033CBF1B190138CCF1200E22FA0CF2E2 +S113773003FA0EF4224323FA0CF30021A7E7821A7A +S113774071EB03033CBF02460B464FF0000141F1CD +S113775000009CE700200021BDE8304000F03DB867 +S11377600423B1EB122F09D2090241EA106100028D +S1137770013B08BF7047B1EB122FF5D30018494104 +S113778034BF9142891A4041494134BF9142891A18 +S11377904041494134BF9142891A4041494134BF73 +S11377A09142891A4041494134BF9142891A40416A +S11377B0494134BF9142891A4041494134BF914201 +S11377C0891A4041494134BF9142891A4041A3F189 +S11377D0010313F00F0FD2D17047704740EA010341 +S11377E0DB0703460CD40346043A22BF51F804CB0A +S11377F043F804CBB2F10402F7D2043208BF704755 +S1137800013A24BF11F801CB03F801CBF8D8704733 +S1137810034613F0030F0ED101F0FF0141EA0121E9 +S113782041EA0141043A24BF43F8041BB2F10402C3 +S1137830F9D202F10402013A24BF03F8011BFAE76A +S113784070474FF0FF3070474FF0FF3070474FF0F4 +S1137850FF3070474FF0FF3070474FF0FF307047F4 +S11378604FF0FF30704710467047FFFF41535345B8 +S11378705254204552524F522022202573200A0090 +S11378802E66736C5F616E61746F705F61692E63E5 +S11378903A323631203A2066616C736500FFFFFF8F +S11378A02E66736C5F636C6F636B2E683A3233378A +S11378B038203A206672657100FFFFFF020503005D +S11378C007160F1C02050300140F09180205030014 +S11378D00F18090D02050300140F0918020503000F +S11378E018090B11020503001018090D0205030005 +S11378F010180B0902050300140F0A1802050300EF +S11379000910180A020503001018090D02050300E6 +S11379101018090D020503000F181A0D02050300C3 +S11379201018090D020503001018090D02050300C3 +S1137930101813140205030010181A1C0205030082 +S113794010181A1C02050300101813140205030072 +S11379501018131402050300101813140205030071 +S113796011090C0F0205030011090C0F0205030095 +S11379701018090D020503001018090D0205030073 +S1137980140F0D18020503001018090D0205030059 +S11379901018090D020503001018090D0205030053 +S11379A01018090D020503001018090D0205030043 +S11379B01018090D020503001018090D0205030033 +S11379C01018090D020503001018090D0205030023 +S11379D01018090D02050300140F0D180205030009 +S11379E0140F0D18020503001018090D02050300F9 +S11379F01018090D020503001018090D02050300F3 +S1137A001018090D02050300140F0D1802050300D8 +S1137A10140F0D18020503001318090D02050300C5 +S1137A201318090D020503001318090D02050300BC +S1137A301318090D02050300140F1318020503009F +S1137A40140F1318020503001018090D0205030092 +S1137A501018090D02050300171A180B020503007C +S1137A60171A180B02050300171A180B0205030056 +S1137A70171A180B02050300171A180B0205030046 +S1137A80171A180B02050300171A180B0205030036 +S1137A900C0A1807020503000C0A18070205030064 +S1137AA018101A0D0205030018101A0D0205030020 +S1137AB0140F1A18020503001A0F130D0205030010 +S1137AC01A13180D020503001A13180D02050300FA +S1137AD01A13180D02050300140F1A1802050300E7 +S1137AE0090B0F1C02050300090C111C02050300FD +S1137AF0090C111C02050300090A111C02050300EC +S1137B00090A111C020503000C0F0A1C02050300DC +S1137B100C0F0A1C020503000C0F0A1C02050300CB +S1137B200C0F121C020503000C09121802050300B5 +S1137B300D01121A2E66736C5F636C6F636B2E6398 +S1137B403A3636203A2028636F6E6669672D3E6C9C +S1137B506F6F7044697669646572203C3D204152C0 +S1137B604D5F504C4C5F4449565F53454C5F4D410B +S1137B7058292026262028636F6E6669672D3E6C7F +S1137B806F6F7044697669646572203E3D2041528E +S1137B904D5F504C4C5F4449565F53454C5F4D49D3 +S1137BA04E2900FF2E66736C5F636C6F636B2E63EC +S1137BB03A323231203A2066726163203C3D2050D3 +S1137BC046445F465241435F4D4158202626206675 +S1137BD0726163203E3D205046445F465241435FFC +S1137BE04D494E002E66736C5F636C6F636B2E633E +S1137BF03A323334203A2066616C736500FFFFFF2C +S1137C002E66736C5F636C6F636B2E633A3235362A +S1137C10203A2028706C6C203D3D206B434C4F4330 +S1137C204B5F506C6C5379733229207C7C20287014 +S1137C306C6C203D3D206B434C4F434B5F506C6C50 +S1137C4053797333290000002E66736C5F636C6F85 +S1137C50636B2E633A323639203A2066616C736561 +S1137C60000000002E66736C5F636C6F636B2E63A1 +S1137C703A323734203A202866726163203E3D2030 +S1137C805046445F465241435F4D494E29202626C3 +S1137C90202866726163203C3D205046445F465272 +S1137CA041435F4D415829002E66736C5F636C6FCE +S1137CB0636B2E633A353430203A206366670000E4 +S1137CC02E66736C5F636C6F636B2E633A3534326C +S1137CD0203A202866726571496E4D687A203C3DD1 +S1137CE020726566467265712920262620286672F0 +S1137CF06571496E4D687A203E3D20313536290044 +S1137D002E66736C5F636C6F636B2E633A3837381F +S1137D10203A2028706C6C203D3D206B434C4F432F +S1137D204B5F506C6C417564696F29207C7C202802 +S1137D30706C6C203D3D206B434C4F434B5F506C4B +S1137D406C566964656F29002E66736C5F636C6F93 +S1137D50636B2E633A393532203A2066616C736561 +S1137D60000000002E66736C5F636C6F636B2E63A0 +S1137D703A393535203A2066726571002E66736C87 +S1137D805F636C6F636B2E633A31303637203A2071 +S1137D9066616C73650000002E66736C5F636C6FC4 +S1137DA0636B2E633A31303730203A206672657146 +S1137DB000FFFFFF2E66736C5F636F6D6D6F6E2E39 +S1137DC0633A323438203A20305520213D2064650E +S1137DD06C61795F757300002E66736C5F636F6D01 +S1137DE06D6F6E2E633A323530203A20636F756EB4 +S1137DF074203C3D2055494E5433325F4D41580068 +S1137E000000000000C01240000013400040134076 +S1137E100080134000C013400000144000C0C5405F +S1137E200000C6400040C6400080C64000C0C640B6 +S1137E300000C7400000CA400080004200C0004269 +S1137E408A33333333333333333333333333FFFF0F +S1137E502E66736C5F6770696F2E633A3537203A0C +S1137E6020696E7374616E6365203C2041525241F7 +S1137E70595F53495A4528735F6770696F4261734C +S1137E80657329002E66736C5F6770696F2E633AA1 +S1137E90313133203A2070696E203C203332550052 +S1137EA00000000000C007400000084000400840F7 +S1137EB00080084000C0084000000940004009401C +S1137EC00080094000C0094000000A400040C24050 +S1137ED00080C2408A565758595A5B5C5D5E5F60A9 +S1137EE061FFFFFF2E66736C5F6C70756172742E98 +S1137EF0633A313236203A20696E7374616E636579 +S1137F00203C2041525241595F53495A4528735FDE +S1137F106C707561727442617365732900FFFFFFB1 +S1137F202E66736C5F6C70756172742E633A3234B2 +S1137F3035203A204E554C4C20213D20636F6E660F +S1137F40696700002E66736C5F6C70756172742EC5 +S1137F50633A323436203A203055203C20636F6E29 +S1137F606669672D3E62617564526174655F427033 +S1137F70730000002E66736C5F6C70756172742EF2 +S1137F80633A323438203A202875696E74385F7445 +S1137F902946534C5F464541545552455F4C505514 +S1137FA04152545F4649464F5F53495A456E2862D1 +S1137FB061736529203E3D20636F6E6669672D3EBF +S1137FC074784669666F57617465726D61726B008F +S1137FD02E66736C5F6C70756172742E633A323402 +S1137FE039203A202875696E74385F742946534CD9 +S1137FF05F464541545552455F4C50554152545F7C +S11380004649464F5F53495A456E28626173652954 +S1138010203E3D20636F6E6669672D3E7278466927 +S1138020666F57617465726D61726B002E66736C56 +S11380305F6C70756172742E633A353231203A2068 +S11380404E554C4C20213D20636F6E66696700FFDE +S11380502E66736C5F6C70756172742E633A383778 +S113806038203A204E554C4C20213D2064617461E7 +S113807000FFFFFF2E66736C5F706D752E633A38D8 +S11380803638203A20636F6E66696720213D204EA2 +S1138090554C4C002E66736C5F706D752E633A38C8 +S11380A03836203A20636F6E66696720213D204E82 +S11380B0554C4C000000000000C007400000084080 +S11380C0004008400080084000C00840000009400B +S11380D0004009400080094000C0094000000A40F7 +S11380E00040C2400080C2402E66736C5F616461D0 +S11380F0707465725F6C70756172742E633A323598 +S113810034203A2068616E646C6500002E66736CDE +S11381105F616461707465725F6C70756172742EF6 +S1138120633A323535203A20636F6E666967000022 +S11381302E66736C5F616461707465725F6C7075D8 +S11381406172742E633A323536203A20636F6E665C +S113815069672D3E696E7374616E6365203C2028E7 +S113816073697A656F6628735F4C707561727441C8 +S11381706461707465724261736529202F2073698C +S11381807A656F66284C50554152545F5479706536 +S1138190202A2929000000002E66736C5F61646147 +S11381A0707465725F6C70756172742E633A3235E7 +S11381B037203A20735F4C7075617274416461704A +S11381C0746572426173655B636F6E6669672D3EA9 +S11381D0696E7374616E63655D00FFFF2E66736C78 +S11381E05F616461707465725F6C70756172742E26 +S11381F0633A333534203A2068616E646C6500005C +S11382002E66736C5F616461707465725F6C707507 +S11382106172742E633A333535203A206461746197 +S1138220000000002E66736C5F6164617074657297 +S11382305F6C70756172742E633A333536203A2060 +S11382406C656E67746800FF433A5C5573657273BE +S11382505C6E786130363534385C446F63756D65B7 +S11382606E74735C4D43555870726573736F4944F3 +S1138270455F31312E332E305F353135385F7072C2 +S11382806332202D20436F70795C776F726B73704B +S11382906163655C65766B6D696D787274313137D5 +S11382A0305F696C65645F626C696E6B795F636D86 +S11382B0375F696E745F52414D5C647269766572B2 +S11382C0732F66736C5F636C6F636B2E683A323323 +S11382D03738203A206672657100FFFF02050300FB +S11382E007160F1C02050300140F091802050300EA +S11382F00F18090D02050300140F091802050300E5 +S113830018090B11020503001018090D02050300DA +S113831010180B0902050300140F0A1802050300C4 +S11383200910180A020503001018090D02050300BC +S11383301018090D020503000F181A0D0205030099 +S11383401018090D020503001018090D0205030099 +S1138350101813140205030010181A1C0205030058 +S113836010181A1C02050300101813140205030048 +S11383701018131402050300101813140205030047 +S113838011090C0F0205030011090C0F020503006B +S11383901018090D020503001018090D0205030049 +S11383A0140F0D18020503001018090D020503002F +S11383B01018090D020503001018090D0205030029 +S11383C01018090D020503001018090D0205030019 +S11383D01018090D020503001018090D0205030009 +S11383E01018090D020503001018090D02050300F9 +S11383F01018090D02050300140F0D1802050300DF +S1138400140F0D18020503001018090D02050300CE +S11384101018090D020503001018090D02050300C8 +S11384201018090D02050300140F0D1802050300AE +S1138430140F0D18020503001318090D020503009B +S11384401318090D020503001318090D0205030092 +S11384501318090D02050300140F13180205030075 +S1138460140F1318020503001018090D0205030068 +S11384701018090D02050300171A180B0205030052 +S1138480171A180B02050300171A180B020503002C +S1138490171A180B02050300171A180B020503001C +S11384A0171A180B02050300171A180B020503000C +S11384B00C0A1807020503000C0A1807020503003A +S11384C018101A0D0205030018101A0D02050300F6 +S11384D0140F1A18020503001A0F130D02050300E6 +S11384E01A13180D020503001A13180D02050300D0 +S11384F01A13180D02050300140F1A1802050300BD +S1138500090B0F1C02050300090C111C02050300D2 +S1138510090C111C02050300090A111C02050300C1 +S1138520090A111C020503000C0F0A1C02050300B2 +S11385300C0F0A1C020503000C0F0A1C02050300A1 +S11385400C0F121C020503000C091218020503008B +S11385500D01121A433A5C55736572735C6E78614F +S113856030363534385C446F63756D656E74735C96 +S11385704D43555870726573736F4944455F31318B +S11385802E332E305F353135385F70726332202DD3 +S113859020436F70795C776F726B73706163655C95 +S11385A065766B6D696D787274313137305F696CE3 +S11385B065645F626C696E6B795F636D375F696E6A +S11385C0745F52414D5C647269766572732F667391 +S11385D06C5F636C6F636B2E683A32323833203AC7 +S11385E020636F6E666967000205030007160F1C9F +S11385F002050300140F0918020503000F18090DE2 +S113860002050300140F09180205030018090B11D1 +S1138610020503001018090D0205030010180B09C8 +S113862002050300140F0A18020503000910180AB2 +S1138630020503001018090D020503001018090DA6 +S1138640020503000F181A0D020503001018090D86 +S1138650020503001018090D020503001018131475 +S11386600205030010181A1C0205030010181A1C36 +S11386700205030010181314020503001018131444 +S113868002050300101813140205030011090C0F4E +S11386900205030011090C0F020503001018090D4F +S11386A0020503001018090D02050300140F0D182C +S11386B0020503001018090D020503001018090D26 +S11386C0020503001018090D020503001018090D16 +S11386D0020503001018090D020503001018090D06 +S11386E0020503001018090D020503001018090DF6 +S11386F0020503001018090D020503001018090DE6 +S113870002050300140F0D1802050300140F0D18C1 +S1138710020503001018090D020503001018090DC5 +S1138720020503001018090D020503001018090DB5 +S113873002050300140F0D1802050300140F0D1891 +S1138740020503001318090D020503001318090D8F +S1138750020503001318090D020503001318090D7F +S113876002050300140F131802050300140F131855 +S1138770020503001018090D020503001018090D65 +S113878002050300171A180B02050300171A180B29 +S113879002050300171A180B02050300171A180B19 +S11387A002050300171A180B02050300171A180B09 +S11387B002050300171A180B020503000C0A180718 +S11387C0020503000C0A18070205030018101A0D0D +S11387D00205030018101A0D02050300140F1A18DD +S11387E0020503001A0F130D020503001A13180DD6 +S11387F0020503001A13180D020503001A13180DBD +S113880002050300140F1A1802050300090B0F1CBC +S113881002050300090C111C02050300090C111CBC +S113882002050300090A111C02050300090A111CB0 +S1138830020503000C0F0A1C020503000C0F0A1C9E +S1138840020503000C0F0A1C020503000C0F121C86 +S1138850020503000C091218020503000D01121A87 +S10B8860030000007200770020 +S107886800A4781FCD +S90334E1E7 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bd b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bd new file mode 100644 index 00000000..46cc0b24 --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bd @@ -0,0 +1,15 @@ +options { + flags = 0x00; + startAddress = 0x1000; + ivtOffset = 0x1000; + initialLoadSize = 0x2000; + + entryPointAddress = 0x34e1; +} + +sources { + elfFile = extern(0); +} + +section (0) { +} diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bin b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bin new file mode 100644 index 0000000000000000000000000000000000000000..9be7365b37ec04955c87e6d2c6d10c40aed37c73 GIT binary patch literal 26732 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 T{wwxMBEBC@WD6lLwoLkWE(i_4 literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.s19 b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.s19 new file mode 100644 index 00000000..e814cad6 --- /dev/null +++ b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.s19 @@ -0,0 +1,1418 @@ +S02B000065766B6D696D787274313137305F696C65645F626C696E6B795F636D375F696E745F52414D2E73311E +S113300000000400E13400002D3500000F6F0000C3 +S11330103135000033350000353500000000000074 +S11330200000000000000000000000003735000030 +S113303039350000000000003B350000A9380000CD +S11330403F680000476800004F68000057680000B0 +S11330505F680000676800006F6800007768000020 +S11330607F680000876800008F6800009768000090 +S11330709F680000A7680000AF680000B768000000 +S1133080BF680000C7680000CF680000D768000070 +S1133090DF680000E7680000EF680000F7680000E0 +S11330A0FF680000076900000F690000176900004D +S11330B01F690000276900002F69000037690000BC +S11330C03F690000476900004F690000576900002C +S11330D05F690000676900006F690000776900009C +S11330E07F690000876900008F690000976900000C +S11330F09F690000A7690000AF690000B76900007C +S1133100BF690000C7690000CF690000D7690000EB +S1133110DF690000E7690000EF690000F76900005B +S1133120FF690000076A00000F6A0000176A0000C8 +S11331301F6A0000276A00002F6A0000376A000037 +S11331403F6A0000476A00004F6A0000576A0000A7 +S11331505F6A0000676A00006F6A0000776A000017 +S11331607F6A0000876A00008F6A0000976A000087 +S11331709F6A0000A76A0000AF6A0000B76A0000F7 +S1133180BF6A0000C76A0000CF6A0000D76A000067 +S1133190DF6A0000E76A0000EF6A0000F76A0000D7 +S11331A0FF6A0000076B00000F6B0000176B000044 +S11331B01F6B0000276B00002F6B0000376B0000B3 +S11331C03F6B0000476B00004F6B0000576B000023 +S11331D05F6B0000676B00006F6B0000776B000093 +S11331E07F6B0000876B00008F6B0000976B000003 +S11331F09F6B0000A76B0000AF6B0000B76B000073 +S1133200BF6B0000C76B0000CF6B0000D76B0000E2 +S1133210DF6B0000E76B0000EF6B0000F76B000052 +S1133220FF6B0000076C00000F6C0000176C0000BF +S11332301F6C0000276C00002F6C0000376C00002E +S11332403F6C0000476C00004F6C0000576C00009E +S11332505F6C0000676C00006F6C0000776C00000E +S11332607F6C0000876C00008F6C0000976C00007E +S11332709F6C0000A76C0000AF6C0000B76C0000EE +S1133280BF6C0000C76C0000CF6C0000D76C00005E +S1133290DF6C0000E76C0000EF6C0000F76C0000CE +S11332A0FF6C0000076D00000F6D0000176D00003B +S11332B01F6D0000276D00002F6D0000376D0000AA +S11332C03F6D0000476D00004F6D0000576D00001A +S11332D05F6D0000676D00006F6D0000776D00008A +S11332E07F6D0000876D00008F6D0000976D0000FA +S11332F09F6D0000A76D0000AF6D0000B76D00006A +S1133300BF6D0000C76D0000CF6D0000D76D0000D9 +S1133310DF6D0000E76D0000EF6D0000F76D000049 +S1133320FF6D0000076E00000F6E0000176E0000B6 +S11333301F6E0000276E00002F6E0000376E000025 +S11333403F6E0000476E00004F6E0000576E000095 +S11333505F6E0000676E00006F6E0000776E000005 +S11333607F6E0000876E00008F6E0000976E000075 +S11333709F6E0000A76E0000AF6E0000B76E0000E5 +S1133380BF6E0000C76E0000CF6E0000D76E000055 +S1133390DF6E0000E76E0000EF6E0000F76E0000C5 +S11333A0FF6E0000076F0000688800006888000056 +S11333B004000000688800000000002000000000F5 +S11333C068880000000024200000000068880000D5 +S11333D000002C2000000000688800000000342059 +S11333E00000000068880000000035200000000094 +S11333F06888000000000080000000006888000069 +S113340000000083000000006C8800001801000028 +S11334100000002000000000000024200000000044 +S113342000002C20000000000000342000000000F8 +S113343000003520000000000000008000000000B3 +S1133440000000830000000030B40B4600249442C6 +S113345005D250F8045B43F8045B0434F7E70C4BE3 +S11334605B6913F4803F11D0002A0FDD01F01F03C4 +S11334701A44BFF34F8F064BC3F868122031203A29 +S1133480002AF8DCBFF34F8FBFF36F8F30BC704757 +S113349000ED00E010B4034600228A4204D2002466 +S11334A043F8044B0432F8E70C4B5B6913F4803F98 +S11334B011D000290FDD00F01F031944BFF34F8F13 +S11334C0064BC3F86802203020390029F8DCBFF32A +S11334D04F8FBFF36F8F5DF8044B704700ED00E032 +S11334E010B572B602F000F90D4B07E003F10C04BD +S11334F09A6859681868FFF7A7FF2346094A934258 +S1133500F4D306E01C46596854F8080BFFF7C2FFD1 +S11335102346054A9342F5D362B603F069FFFEE7FA +S1133520A83300000834000048340000FEE7FEE73A +S1133530FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE75F +S113354080B58AB000AF0346FB7100237B6297F914 +S11335500730042B06D097F90730052B02D03248E8 +S113356000F064F897F90730042B01D1022300E03E +S113357003230021184603F0F2FC38623B6A5B0E19 +S113358003F00703FB613B6A03F07F033B6297F997 +S11335900730042B01D1022300E003233021184615 +S11335A003F0DDFC07EE900AB8EE677B87ED047B41 +S11335B097F90730042B01D1022300E003232021D3 +S11335C0184603F0CCFC07EE900AB8EE677B87ED53 +S11335D0027B3B6A07EE903AB8EE676B97ED024BBD +S11335E097ED045B84EE057B36EE077B9FED0C6B59 +S11335F027EE065B0122FB6902FA03F307EE903A19 +S1133600B8EEE76B85EE067BFCEEC77B17EE903ACF +S11336107B627B6A18462837BD4680BDAFF30080C5 +S11336200000000060E37641007D000080B582B0B8 +S113363000AF78607968024800F0AEF800BEFDE79C +S11336406C78000080B584B000AF786039607B6826 +S1133650002BFCD03B68002BFCD00C4B1B68002BD0 +S113366001D100230DE03A687968094803F008FEA7 +S11336700346FB81FB89002B01D13B6801E04FF03D +S1133680FF3318461037BD4680BD00BF8888000050 +S11336908088000080B58EB000AFB9607B600346BF +S11336A0FB731346BB730023FB867B687B61BB689B +S11336B0BB6100233B7700237B770123BB77012386 +S11336C0FB77FB7B87F82030BB7B87F82C3048F6F0 +S11336D04173FB85BB7B012B03D107F114033B63CF +S11336E017E0BB7B022B03D143F22153FB8610E08E +S11336F0BB7B032B03D143F22153FB8609E0BB7B45 +S1133700042B03D143F22153FB8602E043F22153FD +S1133710FB86FB8E43F2215293422ED01C220021C1 +S1133720184803F06EFE174B174A1A60154B1B68B6 +S113373007F124021146184603F01AFD0346FB86DE +S1133740FB8E002BFCD10F4B1B681049184603F06D +S11337503FFD0346FB86FB8E002BFCD1094B1B6807 +S11337600B49184603F05CFD0346FB86FB8E002BD9 +S1133770FCD1044B1B68074A1360FB8E184638378C +S1133780BD4680BD6C880000708800008088000001 +S113379084880000888800000FB480B5A4B000AF0E +S11337A00023C7F888300023C7F88C3000237B60DF +S11337B007F108037C220021184603F022FE124B75 +S11337C01B68002B16D007F19C03C7F884303A1D00 +S11337D00E4BD7F88410D7F8980002F0E8FEC7F82B +S11337E08800D7F888203B1D11461846FFF72AFFAA +S11337F0C7F88C00D7F88C3018469037BD46BDE822 +S1133800804004B0704700BF8888000081610000D8 +S113381080B483B000AF03463960FB80B7F906304B +S1133820002B0ADB3B68DAB20C49B7F90630120107 +S1133830D2B20B4483F800230AE03B68DAB20849A9 +S1133840FB8803F00F03043B1201D2B20B441A7637 +S113385000BF0C37BD465DF8047B704700E100E013 +S113386000ED00E080B582B000AF78607B68013B7A +S1133870B3F1807F01D301230FE00A4A7B68013B47 +S113388053600F214FF0FF30FFF7C2FF054B0022BA +S11338909A60044B07221A60002318460837BD4675 +S11338A080BD00BF10E000E080B400AF064B1B6891 +S11338B0002B04D0044B1B68013B034A136000BF78 +S11338C0BD465DF8047B70478C88000080B483B0EB +S11338D000AF7860074A7B68136000BF054B1B6824 +S11338E0002BFBD100BF00BF0C37BD465DF8047B45 +S11338F0704700BF8C88000080B500AF02F0E4FA86 +S113390002F0BEF903F00FFD03F0B1FB134B1B688B +S1133910134AA2FB03239B091846FFF7A3FF0346A0 +S1133920002B00D0FEE74FF47A70FFF7CFFF0D4B6A +S11339301B78DBB2002B08D0002203210A4801F0D7 +S113394093F9084B00221A70EDE70122032106487F +S113395001F08AF9034B01221A70E4E76888000039 +S1133960D34D6210808900000040C64080B582B00B +S113397000AF0021002003F0F2FA78607B6840F287 +S11339800512934222D040F205120021002003F0D8 +S1133990D4FA0021002003F0E2FA0E4B4FF48072B7 +S11339A0C3F880250B4BD3F820380A4A23F480331C +S11339B0C2F820380849012001F064F8054BD3F817 +S11339C07035044A43F01003C2F8703500BF08375D +S11339D0BD4680BD0040C84000CA9A3B80B586B051 +S11339E000AF3B600346FB710B46BB7113467B7112 +S11339F0FB79062B00F2008301A252F823F000BFEA +S1133A001D3A0000C93A0000B53B0000B53C000077 +S1133A10B13D00009D3E00004D3F0000BB79002BEE +S1133A2024D0A34BD3F82038A14A23F48033C2F81E +S1133A3020389F4BD3F820387B617B6923F0FF0348 +S1133A407B617B797A6913437B61994A7B69C2F80C +S1133A502038974A3B68C2F83038954BD3F8003584 +S1133A60934A83F48033C2F80035C8E2904BD3F80C +S1133A7020387B617B6923F480337B617B6943F469 +S1133A8080337B618A4A7B69C2F82038884BD3F83B +S1133A9020387B617B6923F0FF037B617B797A6942 +S1133AA013437B61824A7B69C2F82038804BD3F888 +S1133AB000357F4A83F48033C2F800357C4BD3F859 +S1133AC040383B613B699BE2BB79002B34D0784B97 +S1133AD0D3F8503803F40073FB60754BD3F85038B7 +S1133AE0734A23F48033C2F85038714BD3F85038FA +S1133AF07B617B6923F0FF037B617B797A691343E4 +S1133B007B616B4A7B69C2F85038694A3B68C2F8EA +S1133B106038674BD3F85038654A83F48073C2F831 +S1133B205038634BD3F8503803F40073BB60BA6861 +S1133B30FB689A42F5D062E25D4BD3F8503803F447 +S1133B400073FB605A4BD3F850387B617B6923F4D4 +S1133B5080337B617B6943F480337B61544A7B69A6 +S1133B60C2F85038524BD3F850387B617B6923F04C +S1133B70FF037B617B797A6913437B614C4A7B69E0 +S1133B80C2F850384A4BD3F85038494A83F480730A +S1133B90C2F85038464BD3F8503803F40073BB6076 +S1133BA0BA68FB689A42F5D0414BD3F870383B6150 +S1133BB03B6925E2BB79002B3CD03D4BD3F88038E0 +S1133BC003F40073FB603A4BD3F88038384A23F48B +S1133BD08033C2F88038364BD3F880387B617B69F8 +S1133BE023F0FF037B617B797A6913437B61304A5D +S1133BF07B69C2F880382E4A3B68C2F890382C4B57 +S1133C00D3F880382A4A83F48073C2F88038284B6A +S1133C10D3F8803803F40073BB60BA68FB689A4237 +S1133C20F5D0234BD3F88038214A23F48033C2F8EB +S1133C308038E4E11E4BD3F8803803F40073FB6052 +S1133C401B4BD3F880387B617B6923F480337B6121 +S1133C507B6943F480337B61154A7B69C2F8803801 +S1133C60134BD3F880387B617B6923F0FF037B61BE +S1133C707B797A6913437B610D4A7B69C2F880388A +S1133C800B4BD3F880380A4A83F48073C2F8803827 +S1133C90074BD3F8803803F40073BB60BA68FB6841 +S1133CA09A42F5D0024BD3F8A0383B613B69A7E1B7 +S1133CB00040C840BB79002B3CD0A34BD3F8B038AC +S1133CC003F40073FB60A04BD3F8B0389E4A23F48E +S1133CD08033C2F8B0389C4BD3F8B0387B617B6931 +S1133CE023F0FF037B617B797A6913437B61964AF6 +S1133CF07B69C2F8B038944A3B68C2F8C038924B2A +S1133D00D3F8B038904A83F48073C2F8B0388E4B3D +S1133D10D3F8B03803F40073BB60BA68FB689A4206 +S1133D20F5D0894BD3F8B038874A23F48033C2F8EE +S1133D30B03864E1844BD3F8B03803F40073FB600B +S1133D40814BD3F8B0387B617B6923F480337B618A +S1133D507B6943F480337B617B4A7B69C2F8B0386A +S1133D60794BD3F8B0387B617B6923F0FF037B6127 +S1133D707B797A6913437B61734A7B69C2F8B038F3 +S1133D80714BD3F8B038704A83F48073C2F8B038FA +S1133D906D4BD3F8B03803F40073BB60BA68FB68AA +S1133DA09A42F5D0684BD3F8D0383B613B6927E1A0 +S1133DB0BB79002B34D0644BD3F8203903F400735F +S1133DC0FB60614BD3F820395F4A23F48033C2F897 +S1133DD020395D4BD3F820397B617B6923F0FF03E5 +S1133DE07B617B797A6913437B61574A7B69C2F8AB +S1133DF02039554A3B68C2F83039534BD3F820393F +S1133E00514A83F48073C2F820394F4BD3F82039D8 +S1133E1003F40073BB60BA68FB689A42F5D0EEE025 +S1133E20494BD3F8203903F40073FB60464BD3F8B5 +S1133E3020397B617B6923F480337B617B6943F4A4 +S1133E4080337B61404A7B69C2F820393E4BD3F80A +S1133E5020397B617B6923F0FF037B617B797A697D +S1133E6013437B61384A7B69C2F82039364BD3F857 +S1133E702039354A83F48073C2F82039324BD3F8A1 +S1133E80203903F40073BB60BA68FB689A42F5D02A +S1133E902D4BD3F840393B613B69B1E0BB79002B32 +S1133EA024D0294BD3F8E038274A23F48033C2F8CE +S1133EB0E038254BD3F8E0387B617B6923F0FF03BE +S1133EC07B617B797A6913437B611F4A7B69C2F802 +S1133ED0E0381D4A3B68C2F8F0381B4BD3F8003475 +S1133EE0194A83F40043C2F8003488E0164BD3F82F +S1133EF0E0387B617B6923F480337B617B6943F425 +S1133F0080337B61104A7B69C2F8E0380E4BD3F8EA +S1133F10E0387B617B6923F0FF037B617B797A69FD +S1133F2013437B61084A7B69C2F8E038064BD3F837 +S1133F300034054A83F40043C2F80034024BD3F83A +S1133F4010393B613B695BE00040C840BB79002B02 +S1133F5024D02D4BD3F8E0382B4A23F48033C2F815 +S1133F60E038294BD3F8E0387B617B6923F0FF0309 +S1133F707B617B797A6913437B61234A7B69C2F84D +S1133F80E038214A3B68C2F8F0381F4BD3F870354B +S1133F901D4A83F00103C2F8703530E01A4BD3F8A0 +S1133FA0E0387B617B6923F480337B617B6943F474 +S1133FB080337B61144A7B69C2F8E038124BD3F832 +S1133FC0E0387B617B6923F0FF037B617B797A694D +S1133FD013437B610C4A7B69C2F8E0380A4BD3F87F +S1133FE07035094A83F00103C2F87035064BD3F8E3 +S1133FF000393B613B6903E00448FFF717FB0023EA +S113400018461837BD4680BD0040C840807800007F +S113401080B483B000AF0346FB71074AFB79DB0130 +S113402013441B681B0A03F0070318460C37BD46EC +S11340305DF8047B704700BF0000CC4080B483B0BF +S113404000AF0346FB71064AFB79DB0113441B688E +S1134050DBB2013318460C37BD465DF8047B70476C +S11340600000CC4090B5ADF5237D00AF0246FB1DAA +S11340701A7007F108031A4A184611464FF41E73C2 +S11340801A4603F0BCF9FB1D1B781846FFF7C0FF66 +S1134090C7F88402FB1D1B7807F10802DB001A44F1 +S11340A0D7F8843213441B78184600F003FC044606 +S11340B0FB1D1B781846FFF7C1FF0346B4FBF3F35F +S11340C0C7F88032D7F88032002B02D10548FFF7B9 +S11340D0ADFAD7F88032184607F52377BD4690BD70 +S11340E0BC780000A078000080B584B000AF786090 +S11340F07B685B68D02B03D87B685B68672B02D82E +S11341004848FFF793FA484BD3F8003203F40053BE +S1134110002B34D0444BD3F80032DA437B685B681D +S11341201340DBB2002B2AD13F4BD3F80032DA43E1 +S11341307B681B78DB03134003F4C033002B1ED1D0 +S1134140394BD3F8003203F48043002B07D1364BAC +S1134150D3F80032344A43F48043C2F80032324B7D +S1134160D3F8003203F08043002B56D02E4BD3F803 +S113417000322D4A23F08043C2F800324DE0FFF7AD +S1134180F5FB294BD3F8003223F00053FB60FB68A6 +S113419003F4C043002B0BD0FB6823F4C043FB6043 +S11341A0FB6843F08043FB601F4AFB68C2F800329F +S11341B0FA681E4B1340FB607B685B68DAB27B686D +S11341C01B78DB0303F4C0331A43FB681A43184B10 +S11341D01343FB60144AFB68C2F80032BFF34F8FED +S11341E000BFBFF36F8F00BF12491E2000F04AFCCE +S11341F000BF0D4BD3F8003203F00053002BF8D06E +S1134200FB6843F48043FB60FB6823F08043FB605E +S1134210054AFB68C2F8003200E000BF1037BD4613 +S113422080BD00BF347B00000040C840007FFEFF1B +S11342300020004000CA9A3B80B584B000AF78608B +S1134240524BD3F8403203F40003002B2CD04F4BD5 +S1134250D3F880227B685B689A4225D14B4BD3F814 +S113426090227B689B689A421ED1484BD3F8403217 +S113427003F40053002B07D1444BD3F84032434A94 +S113428043F40053C2F84032404BD3F8403203F0B9 +S11342908043002B76D03D4BD3F840323B4A23F089 +S11342A08043C2F840326DE0FFF760FB374BD3F830 +S11342B07032364A43F08033C2F87032334BD3F84D +S11342C04032FB60FA68324B1340002B0BD0FA6883 +S11342D0304B1340FB60FB6843F08043FB602B4A88 +S11342E0FB68C2F84032294B6FF07042C3F8A02239 +S11342F0264A7B685B68C2F88032244A7B689B68E4 +S1134300C2F89032244BFB60204AFB68C2F840326A +S113431022491E2000F0B6FBFA68214B1343FB60D0 +S11343201A4AFB68C2F840321C49FA2000F0AAFB82 +S1134330FB6823F40063FB60144AFB68C2F8403254 +S113434000BF124BD3F8403203F00053B3F1005FC7 +S1134350F7D1FB6843F40053FB600C4AFB68C2F8D6 +S11343604032FB6823F08043FB60084AFB68C2F8D4 +S11343704032064BD3F87032044A03F07F33C2F85C +S1134380703200E000BF1037BD4680BD0040C84019 +S113439000208000FFDF7FFF0800004000CA9A3B36 +S11343A00008800080B586B000AF0346FB710B4661 +S11343B0BB7113467B7100237B6100233B617B79D6 +S11343C0232B02D87B790B2B02D83348FFF72EF925 +S11343D097F90730022B02D0032B05D009E02F4BAD +S11343E07B612F4B3B6107E02E4B7B612E4B3B6186 +S11343F002E02E48FFF71AF97B691A68BB79DB00E3 +S1134400402101FA03F31340FB607B691A68BB790E +S1134410DB00802101FA03F31A437B691A607B698C +S11344201A68BB79DB003F2101FA03F3DB431A402E +S11344307B691A607B691A687B7903F03F01BB7959 +S1134440DB0001FA03F31A437B691A603B691A68BB +S1134450BB79022101FA03F35A403B691A607B6974 +S11344601A68BB79DB00802101FA03F3DB431A40AD +S11344707B691A6000BF7B691A68BB79DB00402145 +S113448001FA03F31A40FB689A42F4D000BF00BF5C +S11344901837BD4680BD00BFA47B00007042C840F1 +S11344A05042C8403042C8402042C840E47B00002B +S11344B080B584B000AF03460A46FB711346BB7156 +S11344C00023FB600023BB6097F90730022B06D062 +S11344D097F90730032B02D02348FFF7A7F897F981 +S11344E00730022B02D0032B0DD019E01F4BD3F859 +S11344F07022BB79DB003F2101FA03F31340BB6058 +S11345001B4BFB600FE0194BD3F83022BB79DB0067 +S11345103F2101FA03F31340BB60164BFB6002E03A +S11345201548FFF783F8BB79DB00BA6822FA03F376 +S1134530BB60BB680B2B02D9BB68232B02D90F4885 +S1134540FFF774F8BB68002B08D0FA68BB68B2FBAD +S1134550F3F21346DB0013445B0000E0002318462B +S11345601037BD4680BD00BF007C00000040C8403D +S113457000A4781F00389C1C487C0000647C000068 +S113458080B584B000AF78603E4BD3F8103203F4AA +S11345900013002B29D03B4BD3F81032DA437B684D +S11345A01B68134003F00703002B1ED1354BD3F8CF +S11345B0103203F40053002B07D1324BD3F81032DE +S11345C0304A43F40053C2F810322E4BD3F8103261 +S11345D003F08043002B50D02A4BD3F81032294AE1 +S11345E023F08043C2F8103247E0FFF7BFF9254BB0 +S11345F0D3F83032234A43F08033C2F830327B6838 +S11346001B6803F00702204B1343FB601D4AFB6841 +S1134610C2F810321D491E2000F034FAFA681C4B0F +S11346201343FB60174AFB68C2F8103217491E2077 +S113463000F028FAFB6823F40063FB60114AFB686E +S1134640C2F8103200BF0F4BD3F8103203F00053FE +S1134650B3F1005FF7D1FA6842F208031343FB6039 +S1134660084AFB68C2F81032FB6823F08043FB6001 +S1134670044AFB68C2F8103200E000BF1037BD46A0 +S113468080BD00BF0040C8401000004000CA9A3BF3 +S11346900008200080B584B000AF786039607B6882 +S11346A0002B02D13348FEF7C1FF4FF41C63FB60BB +S11346B03A68FB689A4202D83B689B2B02D82E4882 +S11346C0FEF7B4FF3A68FB689A4206D37B6803227C +S11346D01A707B68D0225A6046E0FB685B083A682F +S11346E09A420AD37B6803221A703B68234AA2FBCE +S11346F00323DA087B685A6036E0FB689B083A6853 +S11347009A420AD37B6800221A703B681B4AA2FBB8 +S113471003239A087B685A6026E0FB68DB083A6842 +S11347209A420AD37B6801221A703B68134AA2FB9F +S113473003235A087B685A6016E0FB681B093A6831 +S11347409A420BD97B6802221A703B685B000B4AC1 +S1134750A2FB03235A087B685A6005E07B680222A7 +S11347601A707B6868225A60002318461037BD46C9 +S113477080BD00BFA87C0000C07C0000ABAAAAAA30 +S113478080B400AF0A4B1B6A03F01003002B0AD15C +S1134790074B14221A6200BF054B1B6A03F08043C7 +S11347A0B3F1804FF8D100BFBD465DF8047B70477C +S11347B00040C84080B588B000AF0346FB710023B9 +S11347C0FB6197F90730052B59D801A252F823F061 +S11347D0E9470000254800002B4800006548000018 +S11347E06B480000754800002C4BD3F80032DBB254 +S11347F0FB602A4BD3F80032DB0B03F00303BB60EE +S1134800BB680133012202FA03F3BB60BB685B009F +S1134810234AB2FBF3F3FB61FB69FA6802FB03F37F +S1134820FB612FE01F4BFB612CE01C4BD3F8903253 +S113483003F07F03BB61194BD3F8803223F040436C +S11348407B61164BD3F8A03223F040433B617A6975 +S11348503B69B2FBF3F2BB691344114A02FB03F355 +S1134860FB610FE0104BFB610CE00420FEF768FED7 +S1134870F86107E00520FEF763FEF86102E00B48EB +S1134880FEF7D4FEFB69002B02D10948FEF7CEFEE9 +S1134890FB6918462037BD4680BD00BF0040C840B4 +S11348A000366E0100CA9A3B00389C1C487D00000B +S11348B0647D000080B584B000AF0346FB71002323 +S11348C0FB60FB791E2B00F2BD8001A252F823F09D +S11348D04D490000674900006D490000734900001C +S11348E079490000794900007F4900007F490000B0 +S11348F08949000089490000934900009F4900004C +S1134900AB490000B7490000C3490000C349000097 +S1134910454A0000CD490000D9490000E54900009E +S1134920F1490000FD490000FD490000074A00006C +S1134930154A0000294A0000294A0000334A0000B1 +S1134940334A00003D4A00003D4A0000444BD3F87E +S1134950C03003F00403002B02D0424BFB6074E030 +S1134960414BFB6071E0414BFB606EE0404BFB60F0 +S11349706BE0404BFB6068E03D4BFB6065E0002072 +S1134980FFF718FFF86060E00220FFF713FFF860FC +S11349905BE000210220FFF78BFDF86055E0012168 +S11349A00220FFF785FDF8604FE002210220FFF7A7 +S11349B07FFDF86049E003210220FFF779FDF860EC +S11349C043E00320FFF7F6FEF8603EE000210320F9 +S11349D0FFF76EFDF86038E001210320FFF768FD62 +S11349E0F86032E002210320FFF762FDF8602CE05A +S11349F003210320FFF75CFDF86026E00120FFF7A8 +S1134A00D9FEF86021E00120FFF7D4FE03465B08DD +S1134A10FB601AE00120FFF7CDFE0346164AA2FB15 +S1134A2003239B08FB6010E00420FFF7C3FEF8603B +S1134A300BE00520FFF7BEFEF86006E002F0A3FAE3 +S1134A40F86002E00D48FEF7F1FDFB68002B02D18F +S1134A500B48FEF7EBFDFB6818461037BD4680BDDA +S1134A600040C8400024F40000093D00006CDC0252 +S1134A7000366E010084D717CDCCCCCC7C7D0000F1 +S1134A80987D0000F0B585B000AF786039607B6830 +S1134A90002B02D11E48FEF7C9FD7B6818464FF073 +S1134AA000013B681A464FF0000302FB01FC00FBC7 +S1134AB003F66644A0FB020173181946154A4FF029 +S1134AC0000302F0D0FD02460B46C7E90223D7E9F2 +S1134AD00223012B08BF002A02D30F48FEF7A6FDCC +S1134AE0D7E902014FF003024FF0000302F0BBFDCF +S1134AF002460B46941843EB0305C7E90245BB681D +S1134B00184602F061FA00BF1437BD46F0BD00BF7D +S1134B10B47D000040420F00D87D000080B483B013 +S1134B2000AF03460A46FB711346BB71BB791946B5 +S1134B30114AFB7903F540735B0113441B684B4036 +S1134B4003F00103002B10D0BB791A460A49FB7904 +S1134B5002F0010203F540735B010B441A60BFF3DA +S1134B604F8F00BFBFF36F8F00BF00BF0C37BD4630 +S1134B705DF8047B704700BF0000CC4080B584B072 +S1134B8000AF78600023FB6009E00D4AFB6852F82F +S1134B9023307A689A4206D0FB680133FB60FB68D5 +S1134BA00F2BF2D900E000BFFB680F2B02D9054898 +S1134BB0FEF73CFDFB6818461037BD4680BD00BFBC +S1134BC0007E0000507E000080B586B000AFF86023 +S1134BD0B9607A60F868FFF7D1FF78617B690D2BC3 +S1134BE00CD8204A7B6913441B788A2B06D01D4AB3 +S1134BF07B6913441B78184602F0F6F9FB685A697E +S1134C000121BB6801FA03F3DB431A40FB685A61D4 +S1134C107B681B78002B0AD1FB685A680121BB68AA +S1134C2001FA03F3DB431A40FB685A600FE07B6828 +S1134C305B781A46B968F86800F016F8FB685A6899 +S1134C400121BB6801FA03F31A43FB685A607B68CD +S1134C509B781A46B968F86802F0D4F900BF18378F +S1134C60BD4680BD407E000080B584B000AFF860D2 +S1134C70B9601346FB71BB681F2B02D90E48FEF7BF +S1134C80D5FCFB79002B0AD1FB681A680121BB68AB +S1134C9001FA03F3DB431A40FB681A6008E0FB687F +S1134CA01A680121BB6801FA03F31A43FB681A600E +S1134CB000BF1037BD4680BD847E000080B487B03D +S1134CC000AFF860B9601346FB71BB683B61FB68D9 +S1134CD0DA690121BB6801FA03F3DB431A40FB687C +S1134CE0DA61BB680F2B03D8FB680C337B6105E0EA +S1134CF0FB6810337B613B69103B3B61FB79013BF3 +S1134D00042B52D801A252F823F000BF214D000019 +S1134D10394D00005D4D0000814D0000974D0000AD +S1134D207B691A683B695B00032101FA03F3DB43E7 +S1134D301A407B691A6039E07B691A683B695B0039 +S1134D40032101FA03F3DB431A403B695B000121B1 +S1134D5001FA03F31A437B691A6027E07B691A6836 +S1134D603B695B00032101FA03F3DB431A403B690F +S1134D705B00022101FA03F31A437B691A6015E010 +S1134D807B691A683B695B00032101FA03F31A4348 +S1134D907B691A600AE0FB68DA690121BB6801FAE1 +S1134DA003F31A43FB68DA6100E000BF00BF1C375D +S1134DB0BD465DF8047B704780B483B000AF034602 +S1134DC00A46FB711346BB71BB791946114AFB793C +S1134DD003F540735B0113441B684B4003F001036C +S1134DE0002B10D0BB791A460A49FB7902F0010264 +S1134DF003F540735B010B441A60BFF34F8F00BF90 +S1134E00BFF36F8F00BF00BF0C37BD465DF8047B56 +S1134E10704700BF0000CC4080B584B000AF78601C +S1134E200023FB6009E00D4AFB6852F823307A68DE +S1134E309A4206D0FB680133FB60FB680C2BF2D965 +S1134E4000E000BFFB680C2B02D90548FEF7EEFB1F +S1134E50FB6818461037BD4680BD00BFA07E000029 +S1134E60E47E000080B58CB000AFF860B9607A6071 +S1134E70BB68002B02D1A248FEF7D8FBBB681B68B5 +S1134E80002B02D19F48FEF7D1FBBB681B7A042B91 +S1134E9002D99D48FEF7CAFBBB685B7A042B02D992 +S1134EA09A48FEF7C3FB0023FB62BB681B68FB61E7 +S1134EB0002387F823300023FB84042387F822305F +S1134EC041E07A6813469B0013445B001946BB68B3 +S1134ED01B6897F8222002FB03F3B1FBF3F30533BD +S1134EE08B4AA2FB0323DB08BB84BB8C002B01D1C0 +S1134EF00123BB8497F82230BA8C02FB03F37A684F +S1134F00B2FBF3F37B61BB681B687A699A4204D9EC +S1134F10BB681B687A69D31A03E0BB681A687B69AB +S1134F20D31A3B613A69FB699A4207D83B69FB6132 +S1134F3097F8223087F82330BB8CFB8497F8223013 +S1134F40013387F8223097F82230202BB9D9BB6877 +S1134F501B68704AA2FB03235A0913465B001344DF +S1134F60FA699A4203D940F22153FB62FCE0F868E3 +S1134F70FFF752FFB861684ABB6913441B781846AF +S1134F8002F051F8F86802F05CF8FB681B69BB6238 +S1134F9097F82330032B07D997F82330072B03D82E +S1134FA0BB6A43F40033BB62BB6A23F0F853BB62B1 +S1134FB097F82330013B1B0603F0F853BA6A1343F6 +S1134FC0BB62BA6A554B1340BB62FB8CC3F30C0241 +S1134FD0BB6A1A43FB681A61FB681B6923F0005221 +S1134FE0FB681A61FB689A694D4B1340BB62BB684E +S1134FF01B791A46BB68DB7B1B0203F4E0631A438C +S1135000BB689B7B9B0003F004031343BA6A1343FE +S1135010BB62BB685B79012B0DD1BB681B79002B8C +S113502004D0BB6A23F40063BB620CE0BB6A43F4A4 +S11350300063BB6207E0BB681B79002B03D0BB6A2B +S113504043F01003BB62FB68BA6A9A61FB681B6990 +S113505023F40053BB62BB68DB795B0303F40052A7 +S1135060BB6A1A43FB681A61BB685B7A1B04BA68A3 +S1135070127A1A43FB68DA62FB689B6A43F088027F +S1135080FB689A62FB689B6A43F44042FB689A623D +S1135090244BBB62BB6A43F00043BB62BB6A43F46C +S11350A04043BB62FB685A6ABB685B7B1B0103F02D +S11350B01001BB681B7B5B0103F020030B431A4305 +S11350C0FB685A62BB689B7A002B05D0FB685B6A5D +S11350D043F00802FB685A62BB68DB7A002B05D0F8 +S11350E0FB685B6A43F00102FB685A62BB689B7908 +S11350F0002B19D0BB6A43F00053BB6218E000BF19 +S1135100207F0000447F0000747F0000D07F0000F7 +S1135110CDCCCCCC1F85EB51D47E000000E0FFFF4A +S1135120E8F8FFFF00001F40BB6A23F00053BB6296 +S1135130FB685A69BB6A1A43FB685A61FB689B693E +S1135140BB62BB681B7C002B03D0BB6A43F4002307 +S1135150BB62BB685B7C002B03D0BB6A43F4802337 +S1135160BB62FB68BA6A9A61FB6A18463037BD466F +S113517080BD00BF80B582B000AF78607B68002B33 +S113518002D11D48FEF752FA14220021786802F079 +S113519038F97B684FF4E1321A607B6800221A7197 +S11351A07B6800225A717B6800229A717B68002216 +S11351B0DA717B6800221A727B6800225A727B685B +S11351C000229A727B680022DA727B6800225A738A +S11351D07B6800221A737B6800229A737B68002222 +S11351E0DA737B6800221A747B6800225A7400BF49 +S11351F00837BD4680BD00BF2C80000080B586B056 +S113520000AFF860B9607A60BB68002B02D1144823 +S1135210FEF70CFABB687B617B683B6111E000BF61 +S1135220FB685B6903F40003002BF9D07B691B78EE +S11352301A46FB68DA617B6901337B613B69013B98 +S11352403B613B69002BEAD100BFFB685B6903F457 +S11352508003002BF9D0002318461837BD4680BDC3 +S11352605080000080B500AF054B1B68054A5268AA +S1135270114605489847BFF34F8F00BF00BF80BD5C +S1135280C48800009088000000C0074080B500AFCB +S1135290054B1B68054A9268114605489847BFF3B9 +S11352A04F8F00BF00BF80BDC488000090880000FD +S11352B00000084080B500AF054B1B68054AD26862 +S11352C0114605489847BFF34F8F00BF00BF80BD0C +S11352D0C4880000908800000040084080B500AFFA +S11352E0054B1B68054A1269114605489847BFF3E8 +S11352F04F8F00BF00BF80BDC488000090880000AD +S11353000080084080B500AF054B1B68054A526910 +S1135310114605489847BFF34F8F00BF00BF80BDBB +S1135320C48800009088000000C0084080B500AF29 +S1135330054B1B68054A9269114605489847BFF317 +S11353404F8F00BF00BF80BDC4880000908800005C +S11353500000094080B500AF054B1B68054AD269BF +S1135360114605489847BFF34F8F00BF00BF80BD6B +S1135370C4880000908800000040094080B500AF58 +S1135380054B1B68054A126A114605489847BFF346 +S11353904F8F00BF00BF80BDC4880000908800000C +S11353A00080094080B500AF054B1B68054A526A6E +S11353B0114605489847BFF34F8F00BF00BF80BD1B +S11353C0C48800009088000000C0094080B500AF88 +S11353D0054B1B68054A926A114605489847BFF376 +S11353E04F8F00BF00BF80BDC488000090880000BC +S11353F000000A4080B500AF054B1B68054AD26A1D +S1135400114605489847BFF34F8F00BF00BF80BDCA +S1135410C4880000908800000040C24080B584B079 +S113542000AF78600B46FB70FB78002B22D17B68C1 +S1135430D3F8103523F005027B68C3F810252D49F5 +S11354404FF47A70FFF71EFB7B68D3F8103523F412 +S1135450C1527B68C3F8102526494FF47A70FFF7D0 +S113546011FB7B68D3F8103523F400227B68C3F862 +S113547010253BE07B68D3F8103523F001027B68EC +S1135480C3F810251B494FF47A70FFF7FBFA7B68C9 +S1135490D3F8103543F400227B68C3F8102515496E +S11354A04FF47A70FFF7EEFA7B68D3F81035FB609F +S11354B0FB6823F4C053FB60FB78DB0203F4C052A7 +S11354C0FB68134343F02003FB607B68FA68C3F86E +S11354D0102508494FF47A70FFF7D4FA7B68D3F8A3 +S11354E0103543F004027B68C3F8102500BF103761 +S11354F0BD4680BD00CA9A3B80B582B000AF7860DB +S11355000B46FB70FB78002B2FD07B68D3F830352B +S113551043F001027B68C3F8302527494FF47A70C1 +S1135520FFF7B0FA7B68D3F8303543F400327B6878 +S1135530C3F8302520494FF47A70FFF7A3FA7B684B +S1135540D3F8303543F480227B68C3F830251A49F8 +S11355504FF47A70FFF796FA7B68D3F8303523F06E +S113556004027B68C3F8302521E07B68D3F830352A +S113557043F005027B68C3F830250F494FF47A7075 +S1135580FFF780FA7B68D3F8303523F480227B68F8 +S1135590C3F8302508494FF47A70FFF773FA7B6833 +S11355A0D3F8303523F400327B68C3F8302500BFCC +S11355B00837BD4680BD00BF00CA9A3B80B582B0A3 +S11355C000AF78607B68002B02D10A48FEF72EF802 +S11355D003220021786801F014FF7B6801221A700D +S11355E07B6803225A707B680F229A7000BF0837C9 +S11355F0BD4680BD7480000080B584B000AF786083 +S113560039603B68002B02D13448FEF70FF87B6801 +S1135610D3F8503523F070427B68C3F850257B687B +S1135620D3F850253B681B781B071A437B68C3F8E3 +S113563050257B68D3F8503523F070627B68C3F83B +S113564050257B68D3F850253B681B781B0603F074 +S113565070631A437B68C3F850257B68D3F85035D0 +S1135660FB60FA681E4B1340FB603B685B789B0051 +S113567003F01C023B689B785B0103F4F073134353 +S113568043F00203BB60BB68C3F30C03FA68134323 +S1135690FB607B68FA68C3F850257B68D3F86035F3 +S11356A023F4E0527B68C3F860257B68D3F8603547 +S11356B043F480627B68C3F860257B68D3F8603567 +S11356C043F080727B68C3F8602506496420FFF7C5 +S11356D0D9F900BF1037BD4680BD00BF94800000DB +S11356E000E0FFFF00CA9A3B80B584B000AF814B55 +S11356F0D3F888307F4A43F47003C2F888307D4B76 +S11357007D4A9A607D4B1B889BB203F00403002BF7 +S113571007D07A4B1B889BB2784A23F004039BB2D0 +S11357201380774B1B889BB203F00403002B07D034 +S1135730734B1B889BB2724A23F004039BB2138001 +S1135740704B1B6803F40053002B03D06D4B6E4A5F +S11357505A6007E06B4B4CF220525A60694B4DF68D +S113576028125A60674B4FF6FF729A60654B1B68AC +S113577023F0A003634A43F020031360634B1B68C8 +S113578003F40053002B03D0604B5F4A5A6007E0D8 +S11357905E4B4CF220525A605C4B4DF628125A6014 +S11357A05A4B4FF6FF729A60584B1B6823F0A003C4 +S11357B0564A43F020031360554B1B6803F0010362 +S11357C0002B05D0524B1B68514A23F00103136090 +S11357D0484B5B6903F40033B3F5003F22D0454BDB +S11357E05B6903F40033002B1BD1BFF34F8F00BF61 +S11357F0BFF36F8F00BF3F4B0022C3F85022BFF3AB +S11358004F8F00BFBFF36F8F00BF3A4B5B69394ABC +S113581043F400335361BFF34F8F00BFBFF36F8F67 +S113582000E000BF334B5B6903F48033B3F5803F82 +S11358303FD0304B5B6903F48033002B38D12D4BC0 +S11358400022C3F88420BFF34F8F00BF294BD3F845 +S11358508030FB60FB685B0BC3F30E03BB60FB682B +S1135860DB08C3F309037B60BB685A0143F6E073AA +S113587013407A6892071F491343C1F860327B686A +S11358805A1E7A60002BEFD1BB685A1EBA60002BF7 +S1135890E5D1BFF34F8F00BF164B5B69154A43F444 +S11358A080335361BFF34F8F00BFBFF36F8F00E0AE +S11358B000BF184B1B6F174A23F400531367164B92 +S11358C0154A1B69D363144B4FF0FF321A61094B1D +S11358D05B69084A43F010035361BFF34F8F00BF65 +S11358E0BFF36F8F00BF01F0C9FB00BF1037BD4687 +S11358F080BD00BF00ED00E0003000000000034068 +S1135900004003400080034020C528D90000C14066 +S113591010E000E000400E400040C04080B58AB076 +S113592000AF786039600023FB847B68002B02D1D0 +S11359302848FDF77BFE3B68002B02D12648FDF783 +S113594075FE3B681B7B0C2B02D92448FDF76EFEC9 +S11359503B681B7B1A46224B53F82230002B02D1A2 +S11359602048FDF763FE07F108031846FFF702FC21 +S11359703B689B7A002B14BF01230023DBB27B76A8 +S11359803B68DB7A002B14BF01230023DBB23B7698 +S11359903B681B7B1A46124B53F822003B681A687B +S11359A007F108031946FFF75DFA38623B6A002BDA +S11359B005D0386A01F069FB0346FB8405E07B6887 +S11359C0FB613B681A7BFB691A70FB8C184628370D +S11359D0BD4680BDE88000000C81000030810000DD +S11359E0B48000009881000080B586B000AFF860F4 +S11359F0B9607A60FB68002B02D11048FDF716FEEF +S1135A00BB68002B02D10E48FDF710FE7B68002B0B +S1135A1002D10C48FDF70AFEFB687B617B691B78A9 +S1135A201A46094B53F822307A68B9681846FFF7CA +S1135A30E5FB002318461837BD4680BDDC81000015 +S1135A400082000024820000B480000080B483B08F +S1135A5000AF0346FB71074AFB79DB0113441B6863 +S1135A601B0A03F0070318460C37BD465DF8047B98 +S1135A70704700BF0000CC4080B483B000AF034641 +S1135A80FB71064AFB79DB0113441B68DBB201336B +S1135A9018460C37BD465DF8047B70470000CC40C7 +S1135AA090B5ADF5237D00AF0246FB1D1A7007F1DA +S1135AB008031A4A184611464FF41E731A4601F099 +S1135AC09EFCFB1D1B781846FFF7C0FFC7F8840235 +S1135AD0FB1D1B7807F10802DB001A44D7F8843257 +S1135AE013441B781846FEF7E5FE0446FB1D1B789D +S1135AF01846FFF7C1FF0346B4FBF3F3C7F880323F +S1135B00D7F88032002B02D10548FDF78FFDD7F876 +S1135B108032184607F52377BD4690BDDC8200002D +S1135B204882000080B483B000AF0346FB71074A8B +S1135B30FB79DB0113441B681B0A03F007031846B7 +S1135B400C37BD465DF8047B704700BF0000CC40B5 +S1135B5080B483B000AF0346FB71064AFB79DB01D6 +S1135B6013441B68DBB2013318460C37BD465DF89D +S1135B70047B70470000CC4080B582B000AF034680 +S1135B803960FB713B68002B02D11A48FDF74EFDCA +S1135B903B685B781B0503F470023B689B781B042D +S1135BA003F470231A433B68DB781B0203F4E063BD +S1135BB01A433B681B79013BDBB21A433B681B78F1 +S1135BC0002B05D03B681B781B0603F0807300E0B4 +S1135BD000230948F9791A43CB0103441A60BFF33F +S1135BE04F8F00BFBFF36F8F00BF00BF0837BD46A4 +S1135BF080BD00BF548500000000CC4090B5ADF5D9 +S1135C00237D00AF0246FB1D1A7007F108031A4AF0 +S1135C10184611464FF41E731A4601F0F0FBFB1DA3 +S1135C201B781846FFF77EFFC7F88402FB1D1B781C +S1135C3007F10802DB001A44D7F8843213441B78B6 +S1135C401846FEF737FE0446FB1D1B781846FFF77F +S1135C507FFF0346B4FBF3F3C7F88032D7F88032F2 +S1135C60002B02D10548FDF7E1FCD7F88032184635 +S1135C7007F52377BD4690BDE88500004882000003 +S1135C8080B58AB000AF07F1200300221A601A71B0 +S1135C901621724801F058FB07F11C031846FFF760 +S1135CA08DFC07F11C0319466D48FFF7A5FC012183 +S1135CB06B48FFF7B3FB01216948FFF71DFC07F1AF +S1135CC0080300221A605A609A60DA601A6101239C +S1135CD03B7216233B6103237B60FEF751FD0023D7 +S1135CE087F82330012387F8243007F1200319466D +S1135CF00020FFF741FF07F1200319460820FFF7B2 +S1135D003BFF4FF47A7001F046F9042387F82330FF +S1135D10012387F8243007F1200319460020FFF7F8 +S1135D202BFF002387F82330F02387F8243007F172 +S1135D30200319460820FFF71FFF07F10803184640 +S1135D40FEF77AFA3B1D1846FEF71AFC062387F87D +S1135D502330162387F8243007F12003194619202D +S1135D60FFF70AFF102201210220FEF71BFB062386 +S1135D7087F82330032387F8243007F120031946DA +S1135D800420FFF7F9FE042387F82330022387F861 +S1135D90243007F1200319460220FFF7EDFE00230B +S1135DA087F82330012387F8243007F120031946AC +S1135DB02520FFF7E1FE002387F82330012387F82D +S1135DC0243007F1200319462920FFF7D5FE0023CC +S1135DD087F82330012387F8243007F1200319467C +S1135DE00E20FFF7C9FE002387F82330012387F82C +S1135DF0243007F1200319460F20FFF7BDFE0023CE +S1135E0087F82330012387F8243007F1200319464B +S1135E102B20FFF7B1FE002387F82330012387F8F6 +S1135E20243007F1200319460D20FFF7A5FE0023B7 +S1135E3087F82330012387F8243007F1200319461B +S1135E403120FFF799FE0020FFF7D8FE0346054AEC +S1135E50136000BF2837BD4680BD00BF0080CA4024 +S1135E600040C8406888000080B483B000AF034697 +S1135E700A46FB711346BB71BB791946114AFB797B +S1135E8003F540735B0113441B684B4003F00103AB +S1135E90002B10D0BB791A460A49FB7902F00102A3 +S1135EA003F540735B010B441A60BFF34F8F00BFCF +S1135EB0BFF36F8F00BF00BF0C37BD465DF8047B96 +S1135EC0704700BF0000CC4080B584B002AF3120E1 +S1135ED001F063FA01233B7100237B710023BB7142 +S1135EE03B1D1A4603210848FEF76EFE002301936A +S1135EF0064B0093002300220A21054801F05BFAB7 +S1135F0000BF0837BD4680BD0040C64060830E40D8 +S1135F101C810E4010B5174A0020174901F0F0FA11 +S1135F204FF480734FF480620021124801F083FA29 +S1135F30124A1349012001F0E3FA4FF480734FF43D +S1135F40806200210D4801F076FA0E4B1B68022B8B +S1135F500EDD0D4A02200A4901F0D2FA4FF4807393 +S1135F604FF4806200210848BDE8104001F063BA94 +S1135F7010BD00BFCC8800006488000008890000C0 +S1135F806688000060880000448900002DE9F04123 +S1135F90D56880460E461446EB02576905D545F090 +S1135FA01005384601F050FCA061124B2B408BB118 +S1135FB0A169384601F051FC002806DAE3684FF085 +S1135FC0FF3043F08003E36011E025F4003525F051 +S1135FD01005E56032464146384601F044FC20F0A5 +S1135FE00041761AA1690E44A6610028E6D1BDE8F5 +S1135FF0F08100BF10000200F7B5044698B103B069 +S1136000BDE8F04001F00FBB07FB0460013401F070 +S11360100AFB002818BF4FF0FF35019BA342F3DCB5 +S1136020284603B0F0BD034B05463C27024E1B68CF +S11360300193F2E760880000CC8800002DE9F84362 +S1136040C66804463C4B0D46904633401BB90227B4 +S11360503846BDE8F883D0F81490484601F0F7FBC1 +S113606007460028F3D1B8F1010F2CD0B8F1020F84 +S11360702ED0B8F1000FEAD1002DE8DBB00404D52E +S11360802368E26A9A4238BFE362A169A9420EDC3E +S11360902368E26A934238BF134622695818801A6B +S11360A0A84204DB206B01EB000CAC4534DC00237C +S11360B046F02006A562C4E9013326F4032626F03F +S11360C04006E660C4E7204600F038F80544D3E70C +S11360D0484601F0B9FBB0F1000CE26804DA42F082 +S11360E080020127E260B3E723681846A3691946D2 +S11360F0E36A98422CBF0918C91892060B46216915 +S1136100A3EB010303D5A26A9342B8BF13466345C8 +S1136110ACBFED186544AFE76D1AB10744BF281A48 +S1136120A060F10726F0200644BF9B1AEB1A154421 +S113613048BF63602560C0E703001000C3689A0786 +S113614005D10E4B21224FF0FF301A60704703F047 +S113615020021B0309D512B1806A01387047036815 +S1136160826900691344181AF7E70AB1806A704714 +S11361700368826900691344181A7047C8880000CC +S113618080B586B000AFF860B9603B601346FB7120 +S113619000237B6100237B611CE0BB681B68013327 +S11361A07F2B08D9BB681B681946F868FDF74AFAC3 +S11361B0BB6800221A60BB681B681A46FB6813445C +S11361C0FA791A70BB681B685A1CBB681A607B6931 +S11361D001337B617A693B689A42DEDB00BF00BF12 +S11361E01837BD4680BD80B485B000AF7860396093 +S11361F00023FB600023FB7220E07B681B685A1CB1 +S11362007B681A607B681B681B78BB72BB7A2F2B78 +S11362100DD9BB7A392B0AD8FA6813469B0013446C +S11362205B001A46BB7A1344303BFB6006E07B6894 +S11362301B685A1E7B681A600123FB72FB7A002BD1 +S1136240DBD0FB6818461437BD465DF8047B704705 +S113625080B489B000AFF860B9607A60FB681B68ED +S1136260FB610623BB610023FB75FB690133FB6102 +S1136270FB691B782E2B24D10023BB610023FB7503 +S11362801BE0FB690133FB61FB691B78BB75BB7DBC +S11362902F2B0DD9BB7D392B0AD8BA6913469B0025 +S11362A013445B001A46BB7D1344303BBB6104E0DE +S11362B0FB69013BFB610123FB75FB7D002BE0D0F7 +S11362C002E0FB69013BFB61FB68FA691A60BB6988 +S11362D018462437BD465DF8047B704780B485B00A +S11362E000AF0346FB710023FB60FB796F2B08D0E2 +S11362F0FB79622B05D0FB79702B02D0FB79752BCF +S113630001D10123FB60FB6818461437BD465DF8D4 +S1136310047B704780B485B000AF0346FB71002353 +S1136320FB60FB79642B02D0FB79692B01D101233B +S1136330FB60FB6818461437BD465DF8047B704764 +S113634090B585B000AFF860B9607A60FB70BA6848 +S11363507B68D31A7C6A2022F96AB86AA04708E0ED +S11363603B6A5A1E3A621A787C6A0123F96AB86A4F +S1136370A0473B6A1B78002BF2D100BF00BF143743 +S1136380BD4690BD90B585B000AFF860B9607A6045 +S1136390FB70BA687B68D31A7C6A2022F96AB86AEF +S11363A0A04708E03B6A5A1E3A621A787C6A0123C5 +S11363B0F96AB86AA0473B6A1B78002BF2D100BF88 +S11363C000BF1437BD4690BD80B485B000AF03460E +S11363D0FB710023FB60FB79662B02D0FB79462B13 +S11363E001D10123FB60FB6818461437BD465DF8F4 +S11363F0047B704780B485B000AF0346FB71002373 +S1136400FB60FB79782B02D0FB79582B01D1012357 +S1136410FB60FB6818461437BD465DF8047B704783 +S113642080B485B000AF78607B681B68FB60FB6854 +S11364300133FB60FB681B78FB72FB7A682BF6D098 +S1136440FB7A6C2BF3D0FB68013BFB607B68FA683A +S11364501A6000BF1437BD465DF8047B704780B4F2 +S113646085B000AF0346FB71FB796F2B02D1082383 +S1136470FB730DE0FB79622B02D10223FB7307E06F +S1136480FB79702B02D11023FB7301E00A23FB7309 +S1136490FB7B18461437BD465DF8047B704780B417 +S11364A08DB000AFF860B9607A603B600023FB6197 +S11364B0FB68BB61BB695A1CBA6100221A707B6815 +S11364C0002B33D0BB681B68FB62FB6A002B29D10D +S11364D0BB6930221A70FB690133FB61FB6960E020 +S11364E0FA6A3B6892FBF3F33B613B693A6802FB4F +S11364F003F3FA6AD31ABB62BB6A002B04DABB6AE1 +S1136500C3F13003BB6202E0BB6A3033BB623B6958 +S1136510FB62BB695A1CBA61BA6AD2B21A70FB69CF +S11365200133FB61FB6A002BDAD139E0BB681B68DD +S11365307B627B6A002B30D1BB6930221A70FB6905 +S11365400133FB61FB692CE03B687A6AB2FBF3F32D +S11365507B613B687A6902FB03F37A6AD31A3B6274 +S11365603B6A092B03D83B6A30333B620AE097F855 +S11365703830002B01D0412200E061223B6A1344F1 +S11365800A3B3B627B697B62BB695A1CBA613A6A0B +S1136590D2B21A70FB690133FB617B6A002BD3D141 +S11365A0FB6918463437BD465DF8047B704790B5E7 +S11365B09FB004AFF860B9607A603B600023FB656C +S11365C00023BB650023FB61002387F857300023B9 +S11365D07B61FB687B647B6C1B78002B00F029815A +S11365E07B6C1B7887F8563097F85630252B0BD0E8 +S11365F097F8562007F11C013C6801237868A047EE +S11366007B6C01337B6413E1012387F8633007F16A +S1136610080207F1440311461846FFF7E4FD386504 +S113662007F1080107F1440300221846FFF710FEA2 +S1136630F86407F144031846FFF7F2FE7B6C01335C +S11366407B647B6C1B7887F8563097F85630184675 +S1136650FFF760FE0346012B25D1BB681A1DBA6003 +S11366601B68BB6107F1180107F1200097F863303C +S113667000930A230122FFF712FFB86507F12002F5 +S1136680BB6D1344FB65BA6D07F11C0303937B6870 +S113669002933B680193FB6D00930023396D002046 +S11366A0FFF74EFEC1E097F856301846FFF78CFE10 +S11366B00346012B06D1BB68073323F007030833D5 +S11366C0BB60B2E097F856301846FFF793FE0346D6 +S11366D0012B2ED197F85630782B02D1002387F85E +S11366E06330BB681A1DBA601B687B6107F1140133 +S11366F007F1200097F86330009310230022FFF77E +S1136700CEFEB86507F12002BB6D1344FB65BA6D7C +S113671097F8631007F11C0303937B6802933B68AB +S11367200193FB6D00930B46396D0020FFF72AFEA1 +S11367307BE097F856301846FFF7D0FD0346012B4F +S11367402FD1BB681A1DBA601B687B6197F856305D +S11367501846FFF784FE034687F8573097F857200A +S113676007F1140107F1200097F8633000931346F2 +S11367700022FFF794FEB86507F12002BB6D1344B5 +S1136780FB65BA6D07F11C0303937B6802933B68B6 +S11367900193FB6D00930023396D0020FFF7D0FDBA +S11367A043E097F85630632B0DD1BB681A1DBA60CD +S11367B01B68BB64BB6CDAB207F11C013C680123A3 +S11367C07868A04731E097F85630732B25D1BB6821 +S11367D01A1DBA601B687B667B6E002B25D0786E11 +S11367E000F048FE0346BB653A6DBB6DD31A07F152 +S11367F01C013C6820227868A04709E07B6E5A1C83 +S11368007A661A7807F11C013C6801237868A0476E +S11368107B6E1B78002BF1D107E097F8562007F127 +S11368201C013C6801237868A0477B6C01337B64BE +S1136830D1E600BFFB6918466C37BD4690BD08B56C +S1136840FCF77DFE08BD08B5FCF779FE08BD08B568 +S1136850FCF775FE08BD08B5FCF771FE08BD08B568 +S1136860FCF76DFE08BD08B5FCF769FE08BD08B568 +S1136870FCF765FE08BD08B5FCF761FE08BD08B568 +S1136880FCF75DFE08BD08B5FCF759FE08BD08B568 +S1136890FCF755FE08BD08B5FCF751FE08BD08B568 +S11368A0FCF74DFE08BD08B5FCF749FE08BD08B568 +S11368B0FCF745FE08BD08B5FCF741FE08BD08B568 +S11368C0FCF73DFE08BD08B5FCF739FE08BD08B568 +S11368D0FCF735FE08BD08B5FCF731FE08BD08B568 +S11368E0FEF7C0FC08BD08B5FEF7D0FC08BD08B52E +S11368F0FEF7E0FC08BD08B5FEF7F0FC08BD08B5DE +S1136900FEF700FD08BD08B5FEF710FD08BD08B58B +S1136910FEF720FD08BD08B5FEF730FD08BD08B53B +S1136920FEF740FD08BD08B5FEF750FD08BD08B5EB +S1136930FEF760FD08BD08B5FCF701FE08BD08B50B +S1136940FCF7FDFD08BD08B5FCF7F9FD08BD08B569 +S1136950FCF7F5FD08BD08B5FCF7F1FD08BD08B569 +S1136960FCF7EDFD08BD08B5FCF7E9FD08BD08B569 +S1136970FCF7E5FD08BD08B5FCF7E1FD08BD08B569 +S1136980FCF7DDFD08BD08B5FCF7D9FD08BD08B569 +S1136990FCF7D5FD08BD08B5FCF7D1FD08BD08B569 +S11369A0FCF7CDFD08BD08B5FCF7C9FD08BD08B569 +S11369B0FCF7C5FD08BD08B5FCF7C1FD08BD08B569 +S11369C0FCF7BDFD08BD08B5FCF7B9FD08BD08B569 +S11369D0FCF7B5FD08BD08B5FCF7B1FD08BD08B569 +S11369E0FCF7ADFD08BD08B5FCF7A9FD08BD08B569 +S11369F0FCF7A5FD08BD08B5FCF7A1FD08BD08B569 +S1136A00FCF79DFD08BD08B5FCF799FD08BD08B568 +S1136A10FCF795FD08BD08B5FCF791FD08BD08B568 +S1136A20FCF78DFD08BD08B5FCF789FD08BD08B568 +S1136A30FCF785FD08BD08B5FCF781FD08BD08B568 +S1136A40FCF77DFD08BD08B5FCF779FD08BD08B568 +S1136A50FCF775FD08BD08B5FCF771FD08BD08B568 +S1136A60FCF76DFD08BD08B5FCF769FD08BD08B568 +S1136A70FCF765FD08BD08B5FCF761FD08BD08B568 +S1136A80FCF75DFD08BD08B5FCF759FD08BD08B568 +S1136A90FCF755FD08BD08B5FCF751FD08BD08B568 +S1136AA0FCF74DFD08BD08B5FCF749FD08BD08B568 +S1136AB0FCF745FD08BD08B5FCF741FD08BD08B568 +S1136AC0FCF73DFD08BD08B5FCF739FD08BD08B568 +S1136AD0FCF735FD08BD08B5FCF731FD08BD08B568 +S1136AE0FCF72DFD08BD08B5FCF729FD08BD08B568 +S1136AF0FCF725FD08BD08B5FCF721FD08BD08B568 +S1136B00FCF71DFD08BD08B5FCF719FD08BD08B567 +S1136B10FCF715FD08BD08B5FCF711FD08BD08B567 +S1136B20FCF70DFD08BD08B5FCF709FD08BD08B567 +S1136B30FCF705FD08BD08B5FCF701FD08BD08B567 +S1136B40FCF7FDFC08BD08B5FCF7F9FC08BD08B569 +S1136B50FCF7F5FC08BD08B5FCF7F1FC08BD08B569 +S1136B60FCF7EDFC08BD08B5FCF7E9FC08BD08B569 +S1136B70FCF7E5FC08BD08B5FCF7E1FC08BD08B569 +S1136B80FCF7DDFC08BD08B5FCF7D9FC08BD08B569 +S1136B90FCF7D5FC08BD08B5FCF7D1FC08BD08B569 +S1136BA0FCF7CDFC08BD08B5FCF7C9FC08BD08B569 +S1136BB0FCF7C5FC08BD08B5FCF7C1FC08BD08B569 +S1136BC0FCF7BDFC08BD08B5FCF7B9FC08BD08B569 +S1136BD0FCF7B5FC08BD08B5FCF7B1FC08BD08B569 +S1136BE0FCF7ADFC08BD08B5FCF7A9FC08BD08B569 +S1136BF0FCF7A5FC08BD08B5FCF7A1FC08BD08B569 +S1136C00FCF79DFC08BD08B5FCF799FC08BD08B568 +S1136C10FCF795FC08BD08B5FCF791FC08BD08B568 +S1136C20FCF78DFC08BD08B5FCF789FC08BD08B568 +S1136C30FCF785FC08BD08B5FCF781FC08BD08B568 +S1136C40FCF77DFC08BD08B5FCF779FC08BD08B568 +S1136C50FCF775FC08BD08B5FCF771FC08BD08B568 +S1136C60FCF76DFC08BD08B5FCF769FC08BD08B568 +S1136C70FCF765FC08BD08B5FCF761FC08BD08B568 +S1136C80FCF75DFC08BD08B5FCF759FC08BD08B568 +S1136C90FCF755FC08BD08B5FCF751FC08BD08B568 +S1136CA0FCF74DFC08BD08B5FCF749FC08BD08B568 +S1136CB0FCF745FC08BD08B5FCF741FC08BD08B568 +S1136CC0FCF73DFC08BD08B5FCF739FC08BD08B568 +S1136CD0FCF735FC08BD08B5FCF731FC08BD08B568 +S1136CE0FCF72DFC08BD08B5FCF729FC08BD08B568 +S1136CF0FCF725FC08BD08B5FCF721FC08BD08B568 +S1136D00FCF71DFC08BD08B5FCF719FC08BD08B567 +S1136D10FCF715FC08BD08B5FCF711FC08BD08B567 +S1136D20FCF70DFC08BD08B5FCF709FC08BD08B567 +S1136D30FCF705FC08BD08B5FCF701FC08BD08B567 +S1136D40FCF7FDFB08BD08B5FCF7F9FB08BD08B569 +S1136D50FCF7F5FB08BD08B5FCF7F1FB08BD08B569 +S1136D60FCF7EDFB08BD08B5FCF7E9FB08BD08B569 +S1136D70FCF7E5FB08BD08B5FCF7E1FB08BD08B569 +S1136D80FCF7DDFB08BD08B5FCF7D9FB08BD08B569 +S1136D90FCF7D5FB08BD08B5FCF7D1FB08BD08B569 +S1136DA0FCF7CDFB08BD08B5FCF7C9FB08BD08B569 +S1136DB0FCF7C5FB08BD08B5FCF7C1FB08BD08B569 +S1136DC0FCF7BDFB08BD08B5FCF7B9FB08BD08B569 +S1136DD0FCF7B5FB08BD08B5FCF7B1FB08BD08B569 +S1136DE0FCF7ADFB08BD08B5FCF7A9FB08BD08B569 +S1136DF0FCF7A5FB08BD08B5FCF7A1FB08BD08B569 +S1136E00FCF79DFB08BD08B5FCF799FB08BD08B568 +S1136E10FCF795FB08BD08B5FCF791FB08BD08B568 +S1136E20FCF78DFB08BD08B5FCF789FB08BD08B568 +S1136E30FCF785FB08BD08B5FCF781FB08BD08B568 +S1136E40FCF77DFB08BD08B5FCF779FB08BD08B568 +S1136E50FCF775FB08BD08B5FCF771FB08BD08B568 +S1136E60FCF76DFB08BD08B5FCF769FB08BD08B568 +S1136E70FCF765FB08BD08B5FCF761FB08BD08B568 +S1136E80FCF75DFB08BD08B5FCF759FB08BD08B568 +S1136E90FCF755FB08BD08B5FCF751FB08BD08B568 +S1136EA0FCF74DFB08BD08B5FCF749FB08BD08B568 +S1136EB0FCF745FB08BD08B5FCF741FB08BD08B568 +S1136EC0FCF73DFB08BD08B5FCF739FB08BD08B568 +S1136ED0FCF735FB08BD08B5FCF731FB08BD08B568 +S1136EE0FCF72DFB08BD08B5FCF729FB08BD08B568 +S1136EF0FCF725FB08BD08B5FCF721FB08BD08B568 +S1136F00FCF71DFB08BD08B5FCF719FB08BD042000 +S1136F107146084202D0EFF3098001E0EFF30880E4 +S1136F2081690A884BF6AB639A4200D0FEE70231CE +S1136F30816120210160704700BF80B582B000AF3D +S1136F4003463A60FB710B46BB71BA79F8793B682A +S1136F500121FCF743FD00BF0837BD4680BD80B565 +S1136F6084B000AF03460A46FB711346BB71BA797D +S1136F70F87900230021FCF731FDF860FB6818461E +S1136F801037BD4680BD80B500AF0020FDF76AF81C +S1136F900346184680BD80B584B000AF786007F121 +S1136FA0080379681846FDF775FB0346002B06D1E4 +S1136FB007F108031846FDF797F8002300E00123C2 +S1136FC018461037BD4680BD80B483B000AF7860EA +S1136FD07B681846A0F101000028FBD100BF0C37E4 +S1136FE0BD465DF8047B704780B582B000AF0346B0 +S1136FF0FB71FB7901211846FDF790FD00BF0837AE +S1137000BD4680BD80B584B000AFF860B96013465A +S1137010FB71FB791A46B968F868FDF74FFE00BFAB +S11370201037BD4680BD80B582B000AF0346FB710A +S1137030FB7901211846FDF7BFFE00BF0837BD46A6 +S113704080BD80B483B000AF78607B689B6843F0F8 +S113705002027B689A607B689B6823F002027B686B +S11370609A6000BF0C37BD465DF8047B704780B45E +S113707000AF00BFBD465DF8047B704780B400AF2D +S113708000BFBD465DF8047B704780B485B000AF97 +S113709078607B68002B02D10023FB8102E042F67A +S11370A0AF73FB81FB8918461437BD465DF8047B3A +S11370B0704780B483B000AFEFF310833B603B684C +S11370C07B6072B600BF7B6818460C37BD465DF81E +S11370D0047B704780B485B000AF78607B68FB6048 +S11370E0FB6883F3108800BF00BF1437BD465DF80A +S11370F0047B704780B586B000AFF860B9607A60F1 +S11371003B6043F22153FB82FB681B78012B08D1BF +S1137110FB680C333A687968184600F0E0F80346D7 +S1137120FB82FB8A18461837BD4680BD80B586B001 +S113713000AFF860B9607A60FB68002BFCD0BB68D4 +S1137140002BFCD07B68002BFCD0FB687B617B6947 +S11371501B683B613B69002BFCD07B68BA6879698A +S11371603869FFF7C7FF034618461837BD4680BD88 +S113717080B584B000AF7860396043F22153FB815D +S11371803B68002BFCD07B68002BFCD000BF7B68E5 +S1137190BB6010220021B86800F033F93B681A7A0A +S11371A0BB681A703B681B7A012B0BD1BB6803F1D7 +S11371B00C023B68DB681946104600F071F8034680 +S11371C0FB8100E000BFFB8918461037BD4680BD37 +S11371D080B586B000AF786039607B68002BFCD046 +S11371E03B68002BFCD000BF7B687B613B683B6144 +S11371F0FFF75FFFF8607B699B685A1C7B699A60A4 +S1137200F868FFF767FF04220021386800F0F9F8F6 +S11372103B697A691A60002318461837BD4680BD59 +S113722080B586B000AF786039600023FB827B684C +S1137230002BFCD03B68002BFCD000BF7B683B617B +S11372403B68FB60FFF735FFB8603B695B68002B68 +S113725003D043F22253FB820AE03B69FA685A6086 +S113726004220021386800F0CCF8FB683A691A60FF +S1137270B868FFF72FFFFB8A18461837BD4680BD54 +S113728080B584B000AFF860B9607A607A68B96894 +S1137290F868FFF74BFF034618461037BD4680BD1C +S11372A080B584B000AF786039600023FB813B680F +S11372B0002BFCD07B68002BFCD000BF7B68BB603C +S11372C0BB6839681846FEF729FB0346FB81FB8936 +S11372D0002BFCD1FB8918461037BD4680BD80B514 +S11372E086B000AFF860B9607A60FB68002BFCD010 +S11372F0BB68002BFCD07B68002BFCD0FB687B6157 +S11373007B697A68B9681846FEF76EFB034618462F +S11373101837BD4680BD80B500AF1920FEF7C0FB0D +S11373200346184680BD80B582B000AFFFF7F3FF77 +S113733078607B6801224FF4E1310120FCF7AAF95F +S113734000BF0837BD4680BD80B483B000AF78600D +S11373500B46FB707B685B6923F000527B685A61C3 +S11373607B685B6823F4F852FB781B0203F4F85340 +S11373701A437B685B681A437B685A6000BF7B686A +S11373809B6803F00043B3F1004FF8D100BF00BF86 +S11373900C37BD465DF8047B704780B582B000AF02 +S11373A00346FB71FB7901211846FEF75DFD00BF22 +S11373B00837BD4680BD80B485B000AFF860B960C1 +S11373C07A603B60BB6803F00F01FB691B0103F0AB +S11373D01002FB680A431A607B68002B02D07B68AA +S11373E03A681A6000BF1437BD465DF8047B7047E5 +S11373F008B5FEF78FFDBDE80840FCF77DBA00F044 +S1137400EDB900F005BAA8B14FF0805340F80C3C38 +S113741050F8042C72B11368B3F1805F0AD150F8AC +S1137420083C516803330B4440F8083C936840F827 +S1137430043CEDE7704770B5C468A50708D0B2F501 +S1137440007F11D0B2F5806F04D0B2F5807F0BD0ED +S1137450012008E000F124010123224301610160BD +S1137460C361C260002070BD5E1E6FF07F45AE42F6 +S1137470F3D3EDE7421C10B58307044616D1234627 +S113748054F8041BA1F1013020EA010010F0803F00 +S1137490F5D011F0FF0F11D011F47F4F0CD011F47F +S11374A07F0F14BF23460333981A04E010F8013BFE +S11374B0002BE1D1801A10BD0233F5E70133F3E765 +S11374C0C368826923F0200310B5C3600446836A4D +S11374D09A420ED000F048F8E36823F4405323F0B6 +S11374E0100343F01003E360A36AA3612369E3621A +S11374F02360E36823F4804323F04003E36010BD7A +S113750070B50D4606461046144600F056F82B7822 +S1137510722B17D0772B18D0612B23D1082348F274 +S1137520020215F8011F2B2912D0622915D01B075E +S1137530E260666104D5022200212046FEF77EFD4A +S1137540204670BD00230122EBE704230222E8E772 +S113755042F0030243F00203E3E742F0040243F083 +S11375600103DEE70024EBE738B50168044605694A +S1137570C36AC06820F40022E26000F08202022A9A +S113758002D04FF0FF3038BD10F48030FBD0994268 +S113759038BF19468D4208D1E3680020E56223F420 +S11375A080332560A060E360EDE72246491B28464E +S11375B0FEF7ECFC0028EFD0E3E7F0B5C46889B02F +S11375C00546A10722D0220718D4D0E90467FEF7A4 +S11375D013FD384600F035F9230502D53046FFF790 +S11375E012FFA40DA40514F1A54F07D12022296A86 +S11375F0684600F035F9684600F00BF83C2200219B +S11376002846FFF7FEFE002009B0F0BD4FF0FF3022 +S1137610FAE710B50446FFF72DFF01462046BDE802 +S1137620104000F017B9C36870B513F003060446A0 +S113763011D09B0611D5856AFFF742FFE368204607 +S113764023F44053E360FFF78FFF002206462946E8 +S11376502046FEF7F3FC304670BD816905680D4491 +S113766001696D1AEAE75FF0000C30B51C0022D105 +S113767012006FD0914206D305460846002100F05F +S11376806FF80446284600F06BF80A462146BDE828 +S113769030401CF0404F08BF704703D54942404278 +S11376A061F100015FEA8C0C38BF70475B425242C3 +S11376B063F10003704742D41C0C04BF1B040CF19B +S11376C0100C13F07F4F04BF1B020CF1080C13F0D5 +S11376D0704F04BF1B010CF1040C13F0404F04BFA6 +S11376E00CF1020C5FEA83035CBF0CF1010C5B003C +S11376F0CCF1200E22FA0EF4234302FA0CF200FA23 +S11377000CF520FA0EF001FA0CF4204321FA0EF1E4 +S113771014461A4600F024F8A4FB0023AA1A71EBBD +S113772003033CBF1B190138CCF1200E22FA0CF2E2 +S113773003FA0EF4224323FA0CF30021A7E7821A7A +S113774071EB03033CBF02460B464FF0000141F1CD +S113775000009CE700200021BDE8304000F03DB867 +S11377600423B1EB122F09D2090241EA106100028D +S1137770013B08BF7047B1EB122FF5D30018494104 +S113778034BF9142891A4041494134BF9142891A18 +S11377904041494134BF9142891A4041494134BF73 +S11377A09142891A4041494134BF9142891A40416A +S11377B0494134BF9142891A4041494134BF914201 +S11377C0891A4041494134BF9142891A4041A3F189 +S11377D0010313F00F0FD2D17047704740EA010341 +S11377E0DB0703460CD40346043A22BF51F804CB0A +S11377F043F804CBB2F10402F7D2043208BF704755 +S1137800013A24BF11F801CB03F801CBF8D8704733 +S1137810034613F0030F0ED101F0FF0141EA0121E9 +S113782041EA0141043A24BF43F8041BB2F10402C3 +S1137830F9D202F10402013A24BF03F8011BFAE76A +S113784070474FF0FF3070474FF0FF3070474FF0F4 +S1137850FF3070474FF0FF3070474FF0FF307047F4 +S11378604FF0FF30704710467047FFFF41535345B8 +S11378705254204552524F522022202573200A0090 +S11378802E66736C5F616E61746F705F61692E63E5 +S11378903A323631203A2066616C736500FFFFFF8F +S11378A02E66736C5F636C6F636B2E683A3233378A +S11378B038203A206672657100FFFFFF020503005D +S11378C007160F1C02050300140F09180205030014 +S11378D00F18090D02050300140F0918020503000F +S11378E018090B11020503001018090D0205030005 +S11378F010180B0902050300140F0A1802050300EF +S11379000910180A020503001018090D02050300E6 +S11379101018090D020503000F181A0D02050300C3 +S11379201018090D020503001018090D02050300C3 +S1137930101813140205030010181A1C0205030082 +S113794010181A1C02050300101813140205030072 +S11379501018131402050300101813140205030071 +S113796011090C0F0205030011090C0F0205030095 +S11379701018090D020503001018090D0205030073 +S1137980140F0D18020503001018090D0205030059 +S11379901018090D020503001018090D0205030053 +S11379A01018090D020503001018090D0205030043 +S11379B01018090D020503001018090D0205030033 +S11379C01018090D020503001018090D0205030023 +S11379D01018090D02050300140F0D180205030009 +S11379E0140F0D18020503001018090D02050300F9 +S11379F01018090D020503001018090D02050300F3 +S1137A001018090D02050300140F0D1802050300D8 +S1137A10140F0D18020503001318090D02050300C5 +S1137A201318090D020503001318090D02050300BC +S1137A301318090D02050300140F1318020503009F +S1137A40140F1318020503001018090D0205030092 +S1137A501018090D02050300171A180B020503007C +S1137A60171A180B02050300171A180B0205030056 +S1137A70171A180B02050300171A180B0205030046 +S1137A80171A180B02050300171A180B0205030036 +S1137A900C0A1807020503000C0A18070205030064 +S1137AA018101A0D0205030018101A0D0205030020 +S1137AB0140F1A18020503001A0F130D0205030010 +S1137AC01A13180D020503001A13180D02050300FA +S1137AD01A13180D02050300140F1A1802050300E7 +S1137AE0090B0F1C02050300090C111C02050300FD +S1137AF0090C111C02050300090A111C02050300EC +S1137B00090A111C020503000C0F0A1C02050300DC +S1137B100C0F0A1C020503000C0F0A1C02050300CB +S1137B200C0F121C020503000C09121802050300B5 +S1137B300D01121A2E66736C5F636C6F636B2E6398 +S1137B403A3636203A2028636F6E6669672D3E6C9C +S1137B506F6F7044697669646572203C3D204152C0 +S1137B604D5F504C4C5F4449565F53454C5F4D410B +S1137B7058292026262028636F6E6669672D3E6C7F +S1137B806F6F7044697669646572203E3D2041528E +S1137B904D5F504C4C5F4449565F53454C5F4D49D3 +S1137BA04E2900FF2E66736C5F636C6F636B2E63EC +S1137BB03A323231203A2066726163203C3D2050D3 +S1137BC046445F465241435F4D4158202626206675 +S1137BD0726163203E3D205046445F465241435FFC +S1137BE04D494E002E66736C5F636C6F636B2E633E +S1137BF03A323334203A2066616C736500FFFFFF2C +S1137C002E66736C5F636C6F636B2E633A3235362A +S1137C10203A2028706C6C203D3D206B434C4F4330 +S1137C204B5F506C6C5379733229207C7C20287014 +S1137C306C6C203D3D206B434C4F434B5F506C6C50 +S1137C4053797333290000002E66736C5F636C6F85 +S1137C50636B2E633A323639203A2066616C736561 +S1137C60000000002E66736C5F636C6F636B2E63A1 +S1137C703A323734203A202866726163203E3D2030 +S1137C805046445F465241435F4D494E29202626C3 +S1137C90202866726163203C3D205046445F465272 +S1137CA041435F4D415829002E66736C5F636C6FCE +S1137CB0636B2E633A353430203A206366670000E4 +S1137CC02E66736C5F636C6F636B2E633A3534326C +S1137CD0203A202866726571496E4D687A203C3DD1 +S1137CE020726566467265712920262620286672F0 +S1137CF06571496E4D687A203E3D20313536290044 +S1137D002E66736C5F636C6F636B2E633A3837381F +S1137D10203A2028706C6C203D3D206B434C4F432F +S1137D204B5F506C6C417564696F29207C7C202802 +S1137D30706C6C203D3D206B434C4F434B5F506C4B +S1137D406C566964656F29002E66736C5F636C6F93 +S1137D50636B2E633A393532203A2066616C736561 +S1137D60000000002E66736C5F636C6F636B2E63A0 +S1137D703A393535203A2066726571002E66736C87 +S1137D805F636C6F636B2E633A31303637203A2071 +S1137D9066616C73650000002E66736C5F636C6FC4 +S1137DA0636B2E633A31303730203A206672657146 +S1137DB000FFFFFF2E66736C5F636F6D6D6F6E2E39 +S1137DC0633A323438203A20305520213D2064650E +S1137DD06C61795F757300002E66736C5F636F6D01 +S1137DE06D6F6E2E633A323530203A20636F756EB4 +S1137DF074203C3D2055494E5433325F4D41580068 +S1137E000000000000C01240000013400040134076 +S1137E100080134000C013400000144000C0C5405F +S1137E200000C6400040C6400080C64000C0C640B6 +S1137E300000C7400000CA400080004200C0004269 +S1137E408A33333333333333333333333333FFFF0F +S1137E502E66736C5F6770696F2E633A3537203A0C +S1137E6020696E7374616E6365203C2041525241F7 +S1137E70595F53495A4528735F6770696F4261734C +S1137E80657329002E66736C5F6770696F2E633AA1 +S1137E90313133203A2070696E203C203332550052 +S1137EA00000000000C007400000084000400840F7 +S1137EB00080084000C0084000000940004009401C +S1137EC00080094000C0094000000A400040C24050 +S1137ED00080C2408A565758595A5B5C5D5E5F60A9 +S1137EE061FFFFFF2E66736C5F6C70756172742E98 +S1137EF0633A313236203A20696E7374616E636579 +S1137F00203C2041525241595F53495A4528735FDE +S1137F106C707561727442617365732900FFFFFFB1 +S1137F202E66736C5F6C70756172742E633A3234B2 +S1137F3035203A204E554C4C20213D20636F6E660F +S1137F40696700002E66736C5F6C70756172742EC5 +S1137F50633A323436203A203055203C20636F6E29 +S1137F606669672D3E62617564526174655F427033 +S1137F70730000002E66736C5F6C70756172742EF2 +S1137F80633A323438203A202875696E74385F7445 +S1137F902946534C5F464541545552455F4C505514 +S1137FA04152545F4649464F5F53495A456E2862D1 +S1137FB061736529203E3D20636F6E6669672D3EBF +S1137FC074784669666F57617465726D61726B008F +S1137FD02E66736C5F6C70756172742E633A323402 +S1137FE039203A202875696E74385F742946534CD9 +S1137FF05F464541545552455F4C50554152545F7C +S11380004649464F5F53495A456E28626173652954 +S1138010203E3D20636F6E6669672D3E7278466927 +S1138020666F57617465726D61726B002E66736C56 +S11380305F6C70756172742E633A353231203A2068 +S11380404E554C4C20213D20636F6E66696700FFDE +S11380502E66736C5F6C70756172742E633A383778 +S113806038203A204E554C4C20213D2064617461E7 +S113807000FFFFFF2E66736C5F706D752E633A38D8 +S11380803638203A20636F6E66696720213D204EA2 +S1138090554C4C002E66736C5F706D752E633A38C8 +S11380A03836203A20636F6E66696720213D204E82 +S11380B0554C4C000000000000C007400000084080 +S11380C0004008400080084000C00840000009400B +S11380D0004009400080094000C0094000000A40F7 +S11380E00040C2400080C2402E66736C5F616461D0 +S11380F0707465725F6C70756172742E633A323598 +S113810034203A2068616E646C6500002E66736CDE +S11381105F616461707465725F6C70756172742EF6 +S1138120633A323535203A20636F6E666967000022 +S11381302E66736C5F616461707465725F6C7075D8 +S11381406172742E633A323536203A20636F6E665C +S113815069672D3E696E7374616E6365203C2028E7 +S113816073697A656F6628735F4C707561727441C8 +S11381706461707465724261736529202F2073698C +S11381807A656F66284C50554152545F5479706536 +S1138190202A2929000000002E66736C5F61646147 +S11381A0707465725F6C70756172742E633A3235E7 +S11381B037203A20735F4C7075617274416461704A +S11381C0746572426173655B636F6E6669672D3EA9 +S11381D0696E7374616E63655D00FFFF2E66736C78 +S11381E05F616461707465725F6C70756172742E26 +S11381F0633A333534203A2068616E646C6500005C +S11382002E66736C5F616461707465725F6C707507 +S11382106172742E633A333535203A206461746197 +S1138220000000002E66736C5F6164617074657297 +S11382305F6C70756172742E633A333536203A2060 +S11382406C656E67746800FF433A5C5573657273BE +S11382505C6E786130363534385C446F63756D65B7 +S11382606E74735C4D43555870726573736F4944F3 +S1138270455F31312E332E305F353135385F7072C2 +S11382806332202D20436F70795C776F726B73704B +S11382906163655C65766B6D696D787274313137D5 +S11382A0305F696C65645F626C696E6B795F636D86 +S11382B0375F696E745F52414D5C647269766572B2 +S11382C0732F66736C5F636C6F636B2E683A323323 +S11382D03738203A206672657100FFFF02050300FB +S11382E007160F1C02050300140F091802050300EA +S11382F00F18090D02050300140F091802050300E5 +S113830018090B11020503001018090D02050300DA +S113831010180B0902050300140F0A1802050300C4 +S11383200910180A020503001018090D02050300BC +S11383301018090D020503000F181A0D0205030099 +S11383401018090D020503001018090D0205030099 +S1138350101813140205030010181A1C0205030058 +S113836010181A1C02050300101813140205030048 +S11383701018131402050300101813140205030047 +S113838011090C0F0205030011090C0F020503006B +S11383901018090D020503001018090D0205030049 +S11383A0140F0D18020503001018090D020503002F +S11383B01018090D020503001018090D0205030029 +S11383C01018090D020503001018090D0205030019 +S11383D01018090D020503001018090D0205030009 +S11383E01018090D020503001018090D02050300F9 +S11383F01018090D02050300140F0D1802050300DF +S1138400140F0D18020503001018090D02050300CE +S11384101018090D020503001018090D02050300C8 +S11384201018090D02050300140F0D1802050300AE +S1138430140F0D18020503001318090D020503009B +S11384401318090D020503001318090D0205030092 +S11384501318090D02050300140F13180205030075 +S1138460140F1318020503001018090D0205030068 +S11384701018090D02050300171A180B0205030052 +S1138480171A180B02050300171A180B020503002C +S1138490171A180B02050300171A180B020503001C +S11384A0171A180B02050300171A180B020503000C +S11384B00C0A1807020503000C0A1807020503003A +S11384C018101A0D0205030018101A0D02050300F6 +S11384D0140F1A18020503001A0F130D02050300E6 +S11384E01A13180D020503001A13180D02050300D0 +S11384F01A13180D02050300140F1A1802050300BD +S1138500090B0F1C02050300090C111C02050300D2 +S1138510090C111C02050300090A111C02050300C1 +S1138520090A111C020503000C0F0A1C02050300B2 +S11385300C0F0A1C020503000C0F0A1C02050300A1 +S11385400C0F121C020503000C091218020503008B +S11385500D01121A433A5C55736572735C6E78614F +S113856030363534385C446F63756D656E74735C96 +S11385704D43555870726573736F4944455F31318B +S11385802E332E305F353135385F70726332202DD3 +S113859020436F70795C776F726B73706163655C95 +S11385A065766B6D696D787274313137305F696CE3 +S11385B065645F626C696E6B795F636D375F696E6A +S11385C0745F52414D5C647269766572732F667391 +S11385D06C5F636C6F636B2E683A32323833203AC7 +S11385E020636F6E666967000205030007160F1C9F +S11385F002050300140F0918020503000F18090DE2 +S113860002050300140F09180205030018090B11D1 +S1138610020503001018090D0205030010180B09C8 +S113862002050300140F0A18020503000910180AB2 +S1138630020503001018090D020503001018090DA6 +S1138640020503000F181A0D020503001018090D86 +S1138650020503001018090D020503001018131475 +S11386600205030010181A1C0205030010181A1C36 +S11386700205030010181314020503001018131444 +S113868002050300101813140205030011090C0F4E +S11386900205030011090C0F020503001018090D4F +S11386A0020503001018090D02050300140F0D182C +S11386B0020503001018090D020503001018090D26 +S11386C0020503001018090D020503001018090D16 +S11386D0020503001018090D020503001018090D06 +S11386E0020503001018090D020503001018090DF6 +S11386F0020503001018090D020503001018090DE6 +S113870002050300140F0D1802050300140F0D18C1 +S1138710020503001018090D020503001018090DC5 +S1138720020503001018090D020503001018090DB5 +S113873002050300140F0D1802050300140F0D1891 +S1138740020503001318090D020503001318090D8F +S1138750020503001318090D020503001318090D7F +S113876002050300140F131802050300140F131855 +S1138770020503001018090D020503001018090D65 +S113878002050300171A180B02050300171A180B29 +S113879002050300171A180B02050300171A180B19 +S11387A002050300171A180B02050300171A180B09 +S11387B002050300171A180B020503000C0A180718 +S11387C0020503000C0A18070205030018101A0D0D +S11387D00205030018101A0D02050300140F1A18DD +S11387E0020503001A0F130D020503001A13180DD6 +S11387F0020503001A13180D020503001A13180DBD +S113880002050300140F1A1802050300090B0F1CBC +S113881002050300090C111C02050300090C111CBC +S113882002050300090A111C02050300090A111CB0 +S1138830020503000C0F0A1C020503000C0F0A1C9E +S1138840020503000C0F0A1C020503000C0F121C86 +S1138850020503000C091218020503000D01121A87 +S10B8860030000007200770020 +S107886800A4781FCD +S90334E1E7 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/app.bin b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/app.bin new file mode 100644 index 0000000000000000000000000000000000000000..113be30751cd94233c43f62026cdea6abf9cd6f0 GIT binary patch literal 22636 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 F`ClRE3MK#m literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/bdt.bin b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/bdt.bin new file mode 100644 index 0000000000000000000000000000000000000000..f80f3de2fbc8f689245402dc9f4eedf3ab28b118 GIT binary patch literal 12 PcmZP&U|`6pU;qOE2V?;C literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/ivt.bin b/tests/nxpimage/data/hab/evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned/ivt.bin new file mode 100644 index 0000000000000000000000000000000000000000..582f1c5d1adbd635d4e85b1923291bddb308712b GIT binary patch literal 32 Wcmcb}py2S(gaHl|6o5<+h5`Ucm;$E& literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/iee/aes_ctr128/iee_config.yaml b/tests/nxpimage/data/iee/aes_ctr128/iee_config.yaml index b4d3cb24..e4fa4661 100644 --- a/tests/nxpimage/data/iee/aes_ctr128/iee_config.yaml +++ b/tests/nxpimage/data/iee/aes_ctr128/iee_config.yaml @@ -1,8 +1,12 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== IEE: Inline Encryption Engine Configuration template for rt1170. =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == -family: rt1170 # [Required], MCU family, MCU family name., Possible options:['rt1170'] +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt1170'] output_folder: iee_output # [Required], IEE output directory, Path to directory where the IEE output will be generated output_name: encrypted.bin # [Optional], Output binary file name, File name of the encrypted file, output_folder/output_name keyblob_name: iee_keyblob.bin # [Optional], Keyblob file name, File name of the keyblob, output_folder/keyblob_name diff --git a/tests/nxpimage/data/iee/aes_ctr256/iee_config.yaml b/tests/nxpimage/data/iee/aes_ctr256/iee_config.yaml index 6334be7d..b51fba5a 100644 --- a/tests/nxpimage/data/iee/aes_ctr256/iee_config.yaml +++ b/tests/nxpimage/data/iee/aes_ctr256/iee_config.yaml @@ -1,8 +1,12 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== IEE: Inline Encryption Engine Configuration template for rt1170. =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == -family: rt1170 # [Required], MCU family, MCU family name., Possible options:['rt1170'] +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt1170'] output_folder: iee_output # [Required], IEE output directory, Path to directory where the IEE output will be generated input_binary: evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_bootable_nopadding.bin # [Required], Input binary file, Path to input binary file output_name: encrypted.bin # [Optional], Output binary file name, File name of the encrypted file, output_folder/output_name diff --git a/tests/nxpimage/data/iee/aes_xts256/iee_config.yaml b/tests/nxpimage/data/iee/aes_xts256/iee_config.yaml index 52161654..7a751aeb 100644 --- a/tests/nxpimage/data/iee/aes_xts256/iee_config.yaml +++ b/tests/nxpimage/data/iee/aes_xts256/iee_config.yaml @@ -1,8 +1,12 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== IEE: Inline Encryption Engine Configuration template for rt1170. =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == -family: rt1170 # [Required], MCU family, MCU family name., Possible options:['rt1170'] +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt1170'] output_folder: iee_output # [Required], IEE output directory, Path to directory where the IEE output will be generated output_name: encrypted.bin # [Optional], Output binary file name, File name of the encrypted file, output_folder/output_name keyblob_name: iee_keyblob.bin # [Optional], Keyblob file name, File name of the keyblob, output_folder/keyblob_name diff --git a/tests/nxpimage/data/iee/aes_xts512/iee_config.yaml b/tests/nxpimage/data/iee/aes_xts512/iee_config.yaml index 7d047e6e..5f473fe0 100644 --- a/tests/nxpimage/data/iee/aes_xts512/iee_config.yaml +++ b/tests/nxpimage/data/iee/aes_xts512/iee_config.yaml @@ -1,8 +1,12 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== IEE: Inline Encryption Engine Configuration template for rt1170. =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == -family: rt1170 # [Required], MCU family, MCU family name., Possible options:['rt1170'] +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt1170'] output_folder: iee_output # [Required], IEE output directory, Path to directory where the IEE output will be generated keyblob_name: iee_keyblob.bin # [Optional], Keyblob file name, File name of the keyblob, output_folder/keyblob_name output_name: "encrypted.bin" diff --git a/tests/nxpimage/data/iee/aes_xts512_multiple/iee_config.yaml b/tests/nxpimage/data/iee/aes_xts512_multiple/iee_config.yaml index e5b002ff..9d971a39 100644 --- a/tests/nxpimage/data/iee/aes_xts512_multiple/iee_config.yaml +++ b/tests/nxpimage/data/iee/aes_xts512_multiple/iee_config.yaml @@ -1,8 +1,12 @@ ---- # ---------------------------------------------------------------------------------------------------- +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# ---------------------------------------------------------------------------------------------------- # =========== IEE: Inline Encryption Engine Configuration template for rt1170. =========== # ---------------------------------------------------------------------------------------------------- # == Basic Settings == -family: rt1170 # [Required], MCU family, MCU family name., Possible options:['rt1170'] +family: rt117x # [Required], MCU family, MCU family name., Possible options:['rt1170'] output_folder: iee_output # [Required], IEE output directory, Path to directory where the IEE output will be generated keyblob_name: iee_keyblob.bin # [Optional], Keyblob file name, File name of the keyblob, output_folder/keyblob_name output_name: "encrypted.bin" diff --git a/tests/nxpimage/data/iee/aes_xts512_rt1180/blink_fspi2_xip_cm33_ahab.bin b/tests/nxpimage/data/iee/aes_xts512_rt1180/blink_fspi2_xip_cm33_ahab.bin new file mode 100644 index 0000000000000000000000000000000000000000..41262bd66be0eaf2aa9f244ffc3491225c626297 GIT binary patch literal 25028 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 GUi)|PZ}7GN literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/otfad/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_bootable_nopadding.bin b/tests/nxpimage/data/otfad/evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_bootable_nopadding.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7746a7e45adbff6f5231a5099be2550b86f9b20 GIT binary patch literal 26740 zcmeHv3wV=Np7(k4rcH0qK)IBZOWJ@?BM@3DDS;uq5NInFiioy2O&)!Vav2$wEL)VFVcl^)Q;|mjU4x8UFCUshF`;kU?C+fSylq-w z=9}-^{l5L42Yi0{oy-6H&;7jrbEzG~XumL(SP1`T)Wc0N_|LMrWzciWb*lw#wZN?w zxYYu;THsa-+-iYaEpV#^ZneOz7P!>{w_4y<3;h4s0$L;8|IeI8EUDGRG6$RsC4x9qs0p15b z20DSSfiMsSboZldz<6K^Fddi;%mWqyOMr4>jtSOSy-tAT1@9nc7D1-1dZfTw}I zz;nRMz(L?updB~`yaT)sd<=8~Ujtzv3h1hkKQJDc0!#;H1M`4Iz!IPwSPfJI>wrdJ zE3ggN1w0Mx1)c+51`YzR0`0&l;2q$7;A5Z@_!0rxiRj%7&q||{Ns9dP&f1%(*^2M z|0y~NQS=d_=psbX11TSuWa%xhBs<8|9~Q>FyF8+;Dgv}~!<{K*RqnKI-=a|ga(n7W zLTHL;C~m(}{&alV$%r)CILA1j+#dOmhO}R4eA@Dlo+acG3F==F9VF|?LhZX`Dm0a8 zb0V>tvQs}I&@^FClg4N<=9BC}O%EUy*LAe%H<}Q+Hlj=y6uVNi`=tA&bSWBsIcjnw zmhLI+J{=#N=(u0HCz6mH4Zn%dID}q3UGp0;Acn$Ds%#|J3KnKgl&r6jaAqJ#<^8WRbMCX$)%5JQ)Htu?m#s*z$yC(15c zy%^di+IV_Z(!wM^>eYRA3%$RrF;X8it ziqglPsMkM4$UpUUZjfmBP8Oc;^h(6s8?dq8l(G!4&_PTi zg7c+TDRHS7O6W&RZI_>*;WkMKmWQIb=?*>1jkFu-7zBJrzYl${JH1XwH>WxL&BP>s z$18bVe#!45CfioD^;W^;2js1?cUQz{@#FUn%hGQOn&rp*UcoGO=(@&;w%YMQzgGia z(dEpQ@^|#`i37ew__PB)O;C}MzYnh1Q#dOyF$;Z#QaALEFB@NKaE?WdbX`+5qa3sc z4SguxcjWJ*#vQ4M(U;KBMKPg?2u)_8YhpsRR7+`^Q~rLs!^&z-=UxJ@`$RBphtyYm zM_)uM5Yws1;}-h+6n?!IvhHsBT4?>=@q&s#a@8h5O?Ak+^DXi_cuRveEvIDt_j(n%= zd-Ynpg_x7h+oW?xQc9?AwD9hcVoD0q(1a5CaWRxCYHdo4;UUtV0=<}jUTD2Q zf^wp=p?|uD z8uW>w!*Uwr(@K9Pi>CdFw6$J{VjVJ*K!0a>cbg{~ekVGjETiT85n@gVkSsAXX{iv} zb1ea-Hh3;|3J8fu$dlK^G65lf?8N$LJiV+&k9E~Z?7y=s?Lab#5%a&N1+&F|Id1fx z!mI#gCH%b}g+J*TSytZdYdRV6O-n929Wh#yN=@Cq+&AG$C=rYT)|EJfd-AlUMIpWT zCS0VQx0wB3?Brkf*?dR8gTA!US+ptH;op?z@NSY@{E&Lx#p6p^e91tLjse|+ITj$y!|FPX z)pbE&Awqn-uJf7O%IoU0-pTye*VV#u^jT-Iy6(2#!Q^h|Is2?L26PVQY+_-V1G@is zVe){K?95h+TWM{dRX3n}uml=}9bk2T`2xY%rhUDD zjmty4{s$m&$oe3)?lI zd+mjt15&bcJ9A%Oe=pCebufNN{hN6_9hHZyo`Di<9MC;j0tsOOR{!Z2lySZmA;x@V zoV%F2nwQU*Pe-OP|N6SGW;q)3S1I!|e8uRl8a?BafJ(%N6gxT}yoIV^>)}L&I1g+C~{esqM%zu6Trm$Rs z)=8{>AuAo3WbOpyMvC5q>+#^Y!IVSFujeU84#p2De*%w>XBy)MG}3XYXJxGls^fBM zfYu;rH4l_mDgX85HF2$(yu4G8&XYH(bVo>FtGOj;V4AKUTVr_2(Sz|r%A3ODlbFWD z0gbHuth{1SyEGaeFVnAVy7HC9yz)_7j(`8v;$n8G*tE;9*@V#}&lMEhe!5B)@9itrO-IJRQuw(x5nmj3^g?@2Q zz&_cjp*~1y&J#m1teG5?0kI?`_G+O)k$eN+=2zq^S58HIIqw6+tm%kDoqp9red!L} zRnykvj@`%q5p_BcW21VdDk)$VKcKwMtodLm((LJ3ik2cdm`^L|SEQKv&J|IaG&}v? z-`=rkAPg3Om}VEdPvjEwOF89vkn!Z)344dyL7tpk zmPU5=NI7T|+G;(Lg|&{_YGzg&dro#)W5`o@GIAj6HrR$$Xh~nrRJiuTwL52Ond~%j z$t>1Rzma>SOxBhL9zL<82krMunf42*RomE}gO=qbCo6y5MXJLcv_)xOrtp+%%O-F? zorRtpZ7FtqtQIjXP21S$BhinyMX?`X?MxDUM6u&ytvKui=spOxmkDFo+nS8uT#V#d z*XaIGgPo^njte9PXP0?hv&(EQU2qm$v*6MMXF<+z&FeNu{?_xvo+)4_$VLj+c>O|MAXmyKWhK1;10Nx3cpVQGXL^`7+yiE3Gji zFd@iR*eqIRr9NqPKkOITnHrpcKCujo8HX56a4hufh|o00q%73b@AOM(?X!LfEq^u@ zv6~})?7)V(wPcIxRo zN+ag%fQ|X+3WObrovyxgt;thm*bJg;i$2G~XOKRJt}f8PX3+A$?aY_WXOKQ;31(3H zHTvze zx`VSx87zJui@y!==Lh2He$K}ChLtRS9O5$)ss7}%UP;5^t4K1_ph(08m2j^lp**o; z;pgc06Tsj7@|VA)``VdUGrxlMXRnn&h^+=x*)EfK4Qhx+%Q5;;gaA|_c=)d>+WjFnLC&K+d;7)fK`M#fh zbM9;q==X-r8Jt0_W#>?Ie9-lf&VIRdGIq4beMUMn*a$QOtsL9H6i;yM0Y3-) z9q=k}l4J3FV)+ByXMsMBrYUm!-pC|`*Y{}A-k32LXR}X0)gE1F6PTF5VdF+L$Nw@I`2$>k!=1usD1tVLP=L9Is0eiG;>Q zVr8iPSqZCjKabah-n*uL&F!pwbk5T9rD5Lsr75V*NYv&`uZ-P1&Cf6_KZ7#?YwTbs zvv6^$r+iU&Qk}2t8luzYlBILQYIM%!N6U#H$WjVUoZ_HS54O4WHb}P$Hm$jWubiYk zb0O|Gfd_!?z+Qk_DmHf&5rI?N;y~$TJLi% zItEDlM2u49Z1umx*i0E1n<;#3eg*q@VCJ_UKp*{)_5D-4pW5(^p{vBt(DNV2epnyW z_Z)m3)Yqnko}a?^ukckd-#++GVxKD4P-z)f?=H*{_;+gI%CZy$+oUwder9n^a8Q}O zkt`$q@*Ku@EH3xEYz;e~;NR(girzZl)?g!|Cr zK@9pa_PlV}T=U&B;&dO_-1yoeEiHlnDVgaJa%m}OsL}j7OYKo?fW$OM6-w2El;T-C zy?v7Pxe18yY7f zj-EXuZ|PY5J|$1=eNbBId}|fx|qnKPobyXs_{cQyM9a*YE$^_`7VdrDQ1Rk~@ zrJle_O-q@MIc01lmSSKt5CG_RY06_?u6PgQmIR~G&mz$%Aye$M{b?HLcKcZySE9cA z(PJpj!@MoJu@iy6YQXPp#2Wd&~JvH&BQS% zOG=L)H*H?Y<{HJmJ5_Gc#4UYxiSk_}?Y`q{2r!7b;pf}OznmR3}RwSb<8^)USjJk5Kmwvd+R zquLJ2`IT~)t=4UJfaYz_ zc;1$2nXJ}SztVP=X{lV+r^K_Gjy;`2E(XK zO^jcW!B@$HZ%Ryf9zC5?PAqM(9qE^5qQ(X*5i59isG0VihV4S}?Gk&?muf#Mc{}q= z-9a{@oUANP^n#D&JKzubu=1UZIH$#ztfl)ISe@#b%MVdcx`q#)SJK;>$1U4%X5zsa z`{R~J${s1*=X|6r%qE)^${mBVqjxg zLusW`7hGR<*JWcxrPE!u?3)>*E1m1g%D;&=?Q_;a))YD*)|S!{o93*IN$V`DW@*38 z)2`uZD|y=cc-nh;+Ep|y+k3~b(iIbqZ$K{7;)71#qD27<_Yt$TVlb+E{DM>4O16xzCR5`iLNz7lv+Nhj-46aXkImaQF zu_%r2y3(YVWi*b*?Fw0$wr*V6*wPI5>Dn=6cten5a3vyD0$NBf4yi-C5PC}<92fLW z0=_sFLPR^U13uAi{OydhguG#=Zw-ijB*IT%{iAPt=)RQNo60Pkv9vGj?N{c^ZE$aa zJHt!u->L95FrUGZRkjiqA)S>*gx2=cI}dulq3Olghg~wJT`&>T`6J$kY|Ktk{WkbB zyavZ;nx5ETfzp%{kn({Z`d)}yIo5h1YdSt z&`X+#Why}PQTHVO_wJ5_(BJf`H$s2v?x;WfY5$+QJL(HR-8b~^D2RG31^z4QrPzS% zoy{g?G#SwDclXg%G9ymx_OMklLkpM5P=ry^qFU@2A=tN`KSD$4UOg^AneGUD%^16; z#HH6-4Mo2f#Oux<)b}*^ZJ}fT&)v%jp^&V$?O*+gd%6DbyqLD#cEfwQzVLc77H(-3~)2I1KVKXZfdkqCe52H~j)KYoL7Gr|wwAbbSEU%El~ z9EAV4pSJ1;I5CW4Z?crvgE+77{VUc{oc|H^ZPwtu&40yNn&#jfyo;*s!6?AVuov)e zvQ5Gbb7lgpn+B9#*b`qy_7D@vO3IAGIIF_RpZaF%cXsEFk@kt(N80sA$Cnc0_!7*3 zo^reb0%}O+XvpvA8C{yjUSC~|i5Y>Ihwxr2L(-T>el(&~V@yKYcVi?HmLC61Z%5gV z>2b!m7VWX21^3e62CxKt2=~m-Lj`9fpJSwBjN?nqYIoe3#UXv1)ggqI_a1PKE;HM| zd-!=b^{?n9Gvd!(l<3R3qL5F#=o}TC)=l!JLH<)`j-$XqrHXrvAyV*=;d%GS;=gr~ z8UNmK{Ik$@doF7yw;^jorPbLiv8lu&(0sxYR{OYPwMv-TH7zyJn#NR;m8lEe-Hd= zjx6_m=|02H3$^Es>xC~#%HJ=Z`ynwoLdN))YX13iA@{a!@wVGS_V|By{&S}huLG=+ zxZ=~DG}qVVLtP~06G?lnFyzw+(?4TO*9-0mj*Ior7T(dGnEqD!Gx5oeO!o_4 z3Zi)CXUOw4*A*Y^+-NwgD{h}*INk1?+Ft9N<~-3U z5{MxjBO>*MOR+bn>m-e%J8_&ZwHiFsS2@uy(fd}q zFUKtm&XE7@{NR16@0G^}Eg5RfG#Oz#&{STU3a*S6f0Ms$wt2)^e_Ose=>yo}{8SC+ zMQv%t7MtvhSJF(TX$0tyVac4wsY|+po}R1cl1~hr^~umHyW&lLoZ87-WR#8GD`m9I zGEeQm++?8{!NG9<7XKrx1dq@X9COE%;1OB^ze`J{Z8Fw*=zD~ig8>Z>k)1^a_%mB zj!D=t86K`TW?id)t^UB0grKdya`OBdL(9YUf=LOp)zh!`W%eqSw%hLG{~0=z9v-ag zgRIPs8^wO^1CZ19&^z< zTWYc92K=;aH8QN@><+)%f^c|{S$Y(Q>zK8!dgUw`E;l<{Mm{#! z?1?zVwL!bBCMmc#V#TgN2oz+IpexH(pH`e?v(3P|n3y<6PW}7V@t3ZlGg~gtE6&*RIIDIHA7RG7T?ez(!b9!U9P! zwMYk+csF3hSetf%+C8GkpIY}oUlGf%mYv7*_FU`VaPCNKAI@Pi%`t86ebC!L z%+mrb4J5P_rSaFz-{{|X+MuK|X3?Hc40;Fkz+1bR@z6j=PFIJH>b${7>G*e? z^kPhIU@KR`(cbVQ{Rzh@w{qUXc@yW2oZX!3IJ-DM!1;d8t2nRVyo_@R=f#{0I4|U! z&pD6t9LD|O6TP&2{ow%TAm$M8y0vzecXSI^Ht7WoG)?y zob#ugFL3^d^I6VkICpUV1LwCm|CaL`oR4#Uo%2!7hdKX>^Dj8R#QAy7Kjr*m&OhS( z1J2*$`~>I6IB)0t2P~xp2T?q=dqkeaUQ`rk#ihpfpZ@lN4?=|oUd~3;(Uqo=bS&~e1Y>v zoX>JT!?}a=A2`3o`L~?k;C!6(>zt2rKFs-7oPWXjCC<-t{we1lbN&(MA8`I2=O;Kn z#(6vEM>w}~-okkk=Z&1*oa;EdI6uJoe$J~nui(6la|!3goC`QFJ``4^mD;`}`4pK|^&=O1zY0q5^=euDF3oVRm+gmWwB zEu1%T-pJX_xsJ1o^8=jk=e&yZ3eL+omvCOpxq$OR&iS14IM3lclXEubX`C}TPvSfQ ztTk?OBot}_UtdY|3Bj+gB>1+wlO8Yo=I?6v71F;0)6CiKGG+?c>EZ5@{WYZMnD12n zy6RYKq!qSHW^cHtPd!x_{#upF;Pe49bMOLd@T~;B`6R}^aD7C?-|6{|t2b}-jHwlS z6Pz>S#Fv=k@mG!X)TuAL7k`6Cf`UnXZK0e|Qae4}aiB{eWCs1ax50P#YH4Ns1(<-p z9_$P69#%^AZH|l=0`!GXU%0$qJu|;Pbvxpelas-m_v5X)M&p(Cp%nXZ)1kPBlmf3E z2j4Q%w~w(tx>ukzpy%eWX}4>Fh2I=(rhhLwbWcG4Lh`W-f>P4YUMeuMYKsC7(T z`ED1d<7nd@SM-n5op8(^mHl0uvS+h7*hWAyu*KCrZ4c4p2hLS^y7$w)zk45hE4D7GMV?mVc`agdw_zVWc*Z}d4?Xgz zQd6bOR43|+Hjak#qOUn^?kdzp3>00?S}8w@zYoO=(n_bQ`R#}-&4=dK@B(IbG`3A? zdnkQD?-=L{nKIU1YJJr9q9fw^*78gF=&Sy4HoXyHe_7nyF}yYS?wqx>Kc>Bv9#d{M za6SkPgS|u!PQZ!n2^L1@BfTw-hOb2F_!P46LSv3#><#}Cw~a$*0liQ5;w6L$Z*RQp zJzW)UtRodNJHL;tG!!8gf2n~u?3C#|5%Jaqe>uQj`3{*Ye0@=8Y=8KH!5Pa&xH^I{ zE<&jSPFCpq7>z6?6|vVN*wuTb`NMYP)DnKW@Azj4c3AGmNa)h*Boue-=ZDKfzLoY! z$^!iL{vAz+(W}WwX&vJ_ zOpbdZ+KlJB1dH(tf#^fNMOOo(LebPwfl=Mt%2ViX>X>$uL(F~FpbhFHf{mCjbodPV zfApkN>)3&_#E}8)c_J6l>h_f;+yv6wz>Cfo>R)h~+!t$1 zP8#DY!rNtp>W_Plj&Q_dHjTl2x;J8OX)=!wY&E9`TxJtaE0$i?XvRi_Ddz0aftu{G zfehUz&IeuEGaAenBSzg{^%)(W<2YB&`m$4N(qw(sS%|l$^iKOCTw1sU@!vYh)Gs@& zM+Kae#<@?{pOB1r-|&f(gdWvsZr23p*`f8gz8g2Efgj`KbAsc0g$W%OC1U)v1|`j% z<~-oM=)BaK?51u#_=XhS170A-1zZdviyRJW+)@Mac@~)ZU zG-oW>F^`VEIO4^)FpF~C+4T~OrN7CM1*pXg$n@jLNn*7f+>F5mHrHQCQnWM9-4K_a>%j+cUH&UHX)?(6(~8 zH7Q*QaC@j*Na@nTZHGIhOOsOl#SU=|TtZ3}dArLvGOK4~x2Z_elX98#q+B)jj0_1q zDc#1R%$|{5V;lyQW@_X`+;jfYu<1)tT+p5|L9i<6QZ9=~n^k1$Nx4EYU+#RAOVIbn z<#?-H&_&2kI*E~F(wdOa!q>E>!(Wb`o%n8|rr-;`i)aKJts4!ydYYt{6wGb^LE#ey zd%=H>>xC}70SKk0zT1I0y2Sp4AfDHWj+FOAhjxBe`-+J6_xS&5{QgLnRug_#n?p+? z%%9pmDkA(|1R@`x)g9>P)R7%RC@K_uA!GstTF4+>22FSET@8L|Dx}Eh!CF8^&7b?# zJ3LFG(SnsL7gyY4TwGC6USZ5KPHQ$Mky#HmZ>V-PxHdO>s$K3`wexfC#Qzn<_@HY; za~+9Bqb$C5Lu2jwS4_s^{ppFxqo`lc z!}aOOiAsDDlTXyACk>5P(x;{yn4ZCOO1M5fZHyvsVEO9PRaV1?@~Fg=R4T9XaD8J^ zN3e1X5C1>)t9n)bbNW;{mD6~8-HgYirm=FXJY4^HLwYi;_g~5>N$EP8PUUpH(hn++ zNj0#14XJ6Ye++5qH)K^VF^CO4Nk0w_w&0x8sxY|)sP((FT#ShYOC>jhexm%fI${a%a z+_^WIUx>?NmGNxYU|hJ+xW4GF@}jcpB-NdvbM?BTAM>E97oy-t20qturpbY^W%>zj|d!<>DF5)l{a?)m+!i+m5H1Gba~`Jnja_ z=jN;;>>u6+6Ow>(06yRZfOob8Z)k}Rz}r`XcdZ0(R*COT`;+&szVH6Zsx=RMyV~he zzabkuTUQK~sPbdcYySc(`g*mY@h=F-l)d(~Zc(biTbJ#Xs){W)`~e)PuzE->?3 zHLfjdD_ooFstY~n8l~T3S=r4BsaN*i-nJerWu_u_xam|%0pwbtc9Lsbuy zFDH*KMv|*x?S?wuFE@~(eLkeGZy+;R%$eVc8Cx@&-EDP^4`RIE#l~NOQUW@@vW>Hi zJeK$KJ*}QPUdD|egF-9<2vX5FVs1gEmEw%Zd#UH_OaMd z*RXE0M50CW*Q{!;YieH8(BiV(nK#$Erns?o%f`Bf&CP3;6|K6@(^S{o+*ne)xO&c< zS-G<;)p>LBtks^T+8pCdV^O20b8U+HqIG%B5@8Hn|@{p0lY9dCNwOv2j&f^-r^E>#024h8ylz za#a;)@=%Ga-3d!`|K!J+d* z9oH%kAFspxs+`K|xW5^z^FZa{`qc6Lm$Et!R95GK%KshagNW})6WL10i!D?B1L3M1 AGynhq literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/otfad/otfad_rt1160.yaml b/tests/nxpimage/data/otfad/otfad_rt1160.yaml new file mode 100644 index 00000000..9859cc2f --- /dev/null +++ b/tests/nxpimage/data/otfad/otfad_rt1160.yaml @@ -0,0 +1,26 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +--- # ---------------------------------------------------------------------------------------------------- +# =========== On-The-Fly AES decryption Configuration template for rt5xx. =========== +# ---------------------------------------------------------------------------------------------------- +# == Basic Settings == +family: rt116x # [Required], MCU family, MCU family name., Possible options:['rt5xx', 'rt6xx'] +output_folder: otfad_rt1160_outputs # [Required], OTFAD output folder, Folder name to be stored generated OTFAD outputs +# ---------------------------------------------------------------------------------------------------- +# == OTFAD Settings == +# ---------------------------------------------------------------------------------------------------- +kek: kek_inc.bin # [Required], KEK, OTFAD Key Encryption Key to encrypt OTFAD table +otfad_table_address: 0x30000000 # [Required], OTFAD key blobs table address, The base address of key blob table, it should be aligned to 1 KB (1024 B) + +data_blobs: # [Optional], Data blobs list, List of all data blobs included in this key blob + - data: evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_bootable_nopadding.bin # [Required], Plain Text data blob, Path to binary file with plain text data to be encrypted if desired + address: 0x30001000 # [Required], Data blob address, Data blob address, it could be omitted if data blob starts at start_address + +key_blobs: # [Required], List of Key Blobs used by OTFAD, The list of definition of individual key blobs including plain data. Add other array items as you need and device allows + - aes_key: 0x000102030405060708090a0b0c0d0e0f # [Required], AES key, AES key for the key blob + aes_ctr: 0x0123456789abcdef # [Required], AES Counter value, AES counter value for the key blob + start_address: 0x30001000 # [Required], Start address of key blob data, Start address of key blob data, it should be aligned to 1 KB (1024 B) + end_address: 0x30008000 # [Required], End address of key blob data, End address of key blob data, it should be aligned to 1 KB (1024 B) + aes_decryption_enable: true # [Optional], AES decryption enable flag, For accesses hitting in a valid context, this bit indicates if the fetched data is to be decrypted or simply bypassed + valid: true # [Optional], Valid flag, This field signals if the context is valid or not diff --git a/tests/nxpimage/data/otfad/otfad_rt1160_out.bin b/tests/nxpimage/data/otfad/otfad_rt1160_out.bin new file mode 100644 index 0000000000000000000000000000000000000000..703490e4ee5d7302c5bdbddb59042b94c1da8610 GIT binary patch literal 31232 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 O{iOZ>dsOV3Y0 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 O{iOZ>dsO1Rgpp9uPlSWQZ#Ue0001@)hQ_e literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/utils/binary/signed_merge.yaml b/tests/nxpimage/data/utils/binary/signed_merge.yaml new file mode 100644 index 00000000..ce0c15a3 --- /dev/null +++ b/tests/nxpimage/data/utils/binary/signed_merge.yaml @@ -0,0 +1,29 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +# =========== Binary Image Configuration template. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +name: Test of signed merge # [Optional], Image name, The optional image name used just during prints to console during merging. +size: 0 # [Optional], Overall size of image, The overall size of merged image. +pattern: 0xa5 # [Required], Pattern defined as number or special values, The pattern that will be used to fill up gaps between defined regions. +# ---------------------------------------------------------------------------------------------------- +# == Binary images to merge == +# ---------------------------------------------------------------------------------------------------- +regions: # [Optional], Array of defined binary regions, The array of individual regions to merge into final image. + # ---------------------------------------------------------------------------------------------------- + # == List of possible 2 options. Option types[object,object] == + # ---------------------------------------------------------------------------------------------------- + - # [Example of possible configuration #0] + binary_file: # [Required], Binary file + name: My test image with secure addresses # [Optional], Image name, The optional image name used just during prints to console during merging. + path: secure_address_app.hex # [Required], Binary file, The path to binary file to merge into final image + offset: -0x1000_0000 # [Required], Offset of image, The offset of image to be merge on. + - # [Example of possible configuration #1] + binary_block: # [Required], Binary block + name: My super binary block # [Optional], Image name, The optional image name used just during prints to console during merging. + size: 32 # [Required], Binary block size, The size of binary block + offset: 48 # [Required], Offset of binary block, The offset of binary block to be merge on. + pattern: 0xcc # [Required], Pattern defined as number or special values, The pattern that will be used to fill up gaps between defined regions. diff --git a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.bin b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.bin new file mode 100644 index 0000000000000000000000000000000000000000..fdd00e0e75da71bd259d1c7171c55ebf70fb9566 GIT binary patch literal 72 RcmeYe5IC@q@jnBl006z+0?GgY literal 0 HcmV?d00001 diff --git a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.yaml b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.yaml new file mode 100644 index 00000000..dcf7e1d6 --- /dev/null +++ b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_full.yaml @@ -0,0 +1,90 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# =========== External Memory Configuration Data template for rt116x. =========== +# ---------------------------------------------------------------------------------------------------- +# == General Options == +# ---------------------------------------------------------------------------------------------------- +family: rt116x # [Required], MCU family, MCU family name., Possible options:['rt116x', 'rt117x', 'rt118x'] +revision: latest # [Optional], Chip silicon revision, If needed this could be used to specify silicon revision of device., Possible options:['latest'] +mem_type: semc_sdram # [Optional], Memory type, Specify type of memory used by XMCD description., Possible options:['flexspi_ram', 'semc_sdram'] +config_type: full # [Optional], Configuration type, Specify type of configuration used by XMCD description., Possible options:['simplified', 'full'] +xmcd_settings: # [Required], rt116x + header: # [Optional], header, XMCD Header + bitfields: # [Required] + configurationBlockSize: 72 # [Optional], configurationBlockSize, Configuration block size including XMCD header itself + configurationBlockType: 1 # [Optional], configurationBlockType, Configuration block type - 0 - Simplified, 1 - Full + instance: 0 # [Optional], instance, SoC defined instances + memoryInterface: 1 # [Optional], memoryInterface, Memory interface 0 - FlexSPI, 1 - SEMC + version: 0 # [Optional], version, Version, fixed value 0x0 + tag: 12 # [Optional], tag, Tag, fixed value 0xc + magicNumber: # [Optional], magicNumber, Fixed to 0xA1 + value: '0xA1' # [Required], magicNumber, Fixed to 0xA1 + version: # [Optional], version, Set to 1 for this implementation + value: '0x01' # [Required], version, Set to 1 for this implementation + configOption: # [Optional], configOption, Simplified - 0x00, Full - 0xFF - Must be 0xFF in this case + value: '0xFF' # [Required], configOption, Simplified - 0x00, Full - 0xFF - Must be 0xFF in this case + clkMhz: # [Optional], clkMhz, Set the working frequency in the unit of MHz + value: '0x00' # [Required], clkMhz, Set the working frequency in the unit of MHz + sdramSizeKb: # [Optional], sdramSizeKb, Set the memory size of SDRAM CS0 in the unit of kilobytes.Range: 4~4*1024*1024 + value: '0x00000000' # [Required], sdramSizeKb, Set the memory size of SDRAM CS0 in the unit of kilobytes.Range: 4~4*1024*1024 + portSize: # [Optional], portSize, Port size of SDRAM. 0 - 8bit, 1 - 16bit, 2 - 32bit + value: '0x00' # [Required], portSize, Port size of SDRAM. 0 - 8bit, 1 - 16bit, 2 - 32bit + pinConfigPull: # [Optional], pinConfigPull, Pull config of the SDRAM GPIO pin. 0 - Forbidden, 1 - Pull up, 2 - Pull down + value: '0x00' # [Required], pinConfigPull, Pull config of the SDRAM GPIO pin. 0 - Forbidden, 1 - Pull up, 2 - Pull down + pinConfigDriveStrength: # [Optional], pinConfigDriveStrength, Driver config of SDRAM GPIO pin. 0 - High driver, 1 - Normal driver, Others- Invalid + value: '0x00' # [Required], pinConfigDriveStrength, Driver config of SDRAM GPIO pin. 0 - High driver, 1 - Normal driver, Others- Invalid + muxRdy: # [Optional], muxRdy, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + value: '0x00' # [Required], muxRdy, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + muxCsx0: # [Optional], muxCsx0, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + value: '0x00' # [Required], muxCsx0, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + muxCsx1: # [Optional], muxCsx1, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + value: '0x00' # [Required], muxCsx1, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + muxCsx2: # [Optional], muxCsx2, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + value: '0x00' # [Required], muxCsx2, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + muxCsx3: # [Optional], muxCsx3, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + value: '0x00' # [Required], muxCsx3, SDRAM CSn device selection. 1 - SDRAM CS1, 2 - SDRAM CS2, 3 - SDRAM CS3, Others- Invalid for SDRAM + bank: # [Optional], bank, Bank numbers of SDRAM device. 0 - 4 banks, 1 - 2 banks, Others- Invalid + value: '0x00' # [Required], bank, Bank numbers of SDRAM device. 0 - 4 banks, 1 - 2 banks, Others- Invalid + burstLen: # [Optional], burstLen, Burst length.0 - 1, 1 - 2, 2 - 4, 3 - 8, Others- Invalid + value: '0x00' # [Required], burstLen, Burst length.0 - 1, 1 - 2, 2 - 4, 3 - 8, Others- Invalid + columnAddrBitNum: # [Optional], columnAddrBitNum, Column address bit number.0 - 12 bit, 1 - 11 bit, 2 - 10 bit, 3 - 9 bit, 4 - 8 bit, Others- Invalid + value: '0x00' # [Required], columnAddrBitNum, Column address bit number.0 - 12 bit, 1 - 11 bit, 2 - 10 bit, 3 - 9 bit, 4 - 8 bit, Others- Invalid + casLatency: # [Optional], casLatency, CAS Latency, 1 - 1, 2 - 2, 3 - 3, Others- Invalid + value: '0x00' # [Required], casLatency, CAS Latency, 1 - 1, 2 - 2, 3 - 3, Others- Invalid + writeRecoveryNs: # [Optional], writeRecoveryNs, Write recovery time in unit of nanosecond. This could help to meet tWR timing requirement by the SDRAM device. + value: '0x00' # [Required], writeRecoveryNs, Write recovery time in unit of nanosecond. This could help to meet tWR timing requirement by the SDRAM device. + refreshRecoveryNs: # [Optional], refreshRecoveryNs, Refresh recovery time in unit of nanosecond. This could help to meet tRFC timing requirement by the SDRAM device. + value: '0x00' # [Required], refreshRecoveryNs, Refresh recovery time in unit of nanosecond. This could help to meet tRFC timing requirement by the SDRAM device. + act2readwriteNs: # [Optional], act2readwriteNs, Act to read/write wait time in unit of nanosecond. This could help to meet tRCD timing requirement by the SDRAM device. + value: '0x00' # [Required], act2readwriteNs, Act to read/write wait time in unit of nanosecond. This could help to meet tRCD timing requirement by the SDRAM device. + precharge2actNs: # [Optional], precharge2actNs, Precharge to active wait time in unit of nanosecond. This could help to meet tRP timing requirement by SDRAM device. + value: '0x00' # [Required], precharge2actNs, Precharge to active wait time in unit of nanosecond. This could help to meet tRP timing requirement by SDRAM device. + act2actBanksNs: # [Optional], act2actBanksNs, Active to active wait time between two different banks in unit of nanosecond. This could help to meet tRRD timing requirement by the SDRAM device. + value: '0x00' # [Required], act2actBanksNs, Active to active wait time between two different banks in unit of nanosecond. This could help to meet tRRD timing requirement by the SDRAM device. + refresh2refreshNs: # [Optional], refresh2refreshNs, Auto refresh to auto refresh wait time in unit of nanosecond. This could help to meet tRFC timing requirement by the SDRAM device. + value: '0x00' # [Required], refresh2refreshNs, Auto refresh to auto refresh wait time in unit of nanosecond. This could help to meet tRFC timing requirement by the SDRAM device. + selfrefRecoveryNs: # [Optional], selfrefRecoveryNs, Self refresh recovery time in unit of nanosecond. This could help to meet tXSR timing requirement by the SDRAM device. + value: '0x00' # [Required], selfrefRecoveryNs, Self refresh recovery time in unit of nanosecond. This could help to meet tXSR timing requirement by the SDRAM device. + act2prechargeMinNs: # [Optional], act2prechargeMinNs, ACT to Precharge minimum time in unit of nanosecond. This could help to meet tRAS(max) timing requirement by the SDRAM device. + value: '0x00' # [Required], act2prechargeMinNs, ACT to Precharge minimum time in unit of nanosecond. This could help to meet tRAS(max) timing requirement by the SDRAM device. + act2prechargeMaxNs: # [Optional], act2prechargeMaxNs, ACT to Precharge maximum time in unit of nanosecond. This could help to meet tRAS(max) timing requirement by the SDRAM device. + value: '0x00000000' # [Required], act2prechargeMaxNs, ACT to Precharge maximum time in unit of nanosecond. This could help to meet tRAS(max) timing requirement by the SDRAM device. + refreshperiodPerrowNs: # [Optional], refreshperiodPerrowNs, Refresh timer period in unit of nanosecond. Set to (tREF(ms) * 1000000/rows) value. + value: '0x00000000' # [Required], refreshperiodPerrowNs, Refresh timer period in unit of nanosecond. Set to (tREF(ms) * 1000000/rows) value. + modeRegister: # [Optional], modeRegister, Define the specific mode of operation of SDRAM. Set to the value required by SDRAM device. + value: '0x00000000' # [Required], modeRegister, Define the specific mode of operation of SDRAM. Set to the value required by SDRAM device. + sdram0Base: # [Optional], sdram0Base, Base address of SDRAM CS0. Range: 0x80000000~0xDFFFFFFF. + value: '0x00000000' # [Required], sdram0Base, Base address of SDRAM CS0. Range: 0x80000000~0xDFFFFFFF. + sdram1Base: # [Optional], sdram1Base, Base address of SDRAM CS1. Range: 0x80000000~0xDFFFFFFF. If CS1 is not being used, set the address to 0. + value: '0x00000000' # [Required], sdram1Base, Base address of SDRAM CS1. Range: 0x80000000~0xDFFFFFFF. If CS1 is not being used, set the address to 0. + sdram2Base: # [Optional], sdram2Base, Base address of SDRAM CS2. Range: 0x80000000~0xDFFFFFFF. If CS2 is not being used, set the address to 0. + value: '0x00000000' # [Required], sdram2Base, Base address of SDRAM CS2. Range: 0x80000000~0xDFFFFFFF. If CS2 is not being used, set the address to 0. + sdram3Base: # [Optional], sdram3Base, Base address of SDRAM CS3. Range: 0x80000000~0xDFFFFFFF. If CS3 is not being used, set the address to 0. + value: '0x00000000' # [Required], sdram3Base, Base address of SDRAM CS3. Range: 0x80000000~0xDFFFFFFF. If CS3 is not being used, set the address to 0. + sdram1SizeKb: # [Optional], sdram1SizeKb, Set the memory size of SDRAM CS1 in unit of kbytes. Range: 4~4*1024*1024 + value: '0x00000000' # [Required], sdram1SizeKb, Set the memory size of SDRAM CS1 in unit of kbytes. Range: 4~4*1024*1024 + sdram2SizeKb: # [Optional], sdram2SizeKb, Set the memory size of SDRAM CS2 in unit of kbytes. Range: 4~4*1024*1024 + value: '0x00000000' # [Required], sdram2SizeKb, Set the memory size of SDRAM CS2 in unit of kbytes. Range: 4~4*1024*1024 + sdram3SizeKb: # [Optional], sdram3SizeKb, Set the memory size of SDRAM CS3 in unit of kbytes. Range: 4~4*1024*1024 + value: '0x00000000' # [Required], sdram3SizeKb, Set the memory size of SDRAM CS3 in unit of kbytes. Range: 4~4*1024*1024 diff --git a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified.bin b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_0.bin similarity index 100% rename from tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified.bin rename to tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_0.bin diff --git a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified.yaml b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_0.yaml similarity index 93% rename from tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified.yaml rename to tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_0.yaml index d2ea8312..cb022e68 100644 --- a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified.yaml +++ b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_0.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # =========== External Memory Configuration Data template for rt116x. =========== # ---------------------------------------------------------------------------------------------------- # == General Options == @@ -15,7 +19,7 @@ xmcd_settings: # [Required], rt116x memoryInterface: 0 # [Optional], memoryInterface, Memory interface 0 - FlexSPI, 1 - SEMC version: 0 # [Optional], version, Version, fixed value 0x0 tag: 12 # [Optional], tag, Tag, fixed value 0xc - configBlock: # [Optional], configBlock, XMCD Configuration Block + configOption0: # [Optional], configOption0, XMCD Configuration Option 0 bitfields: # [Required] sizeInMB: 0 # [Optional], sizeInMB, Size in MB. 0 - Auto detection, Others - Size in MB maximumFrequency: 7 # [Optional], maximumFrequency, Maximum frequency. SoC specific definitions diff --git a/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_1.bin b/tests/nxpimage/data/xmcd/rt116x/flexspi_ram_simplified_1.bin new file mode 100644 index 0000000000000000000000000000000000000000..6e5e33bac21562b466463e7930e90c2a55b431f7 GIT binary patch literal 12 Rcmd;KU^u|Q&Tx Tuple[str, str, str]: + with open(config_path) as f: + config_data = json.load(f) + for key in config_data: + if isinstance(config_data[key], str): + config_data[key] = config_data[key].replace("\\", "/") + ref_binary = config_data[config_member] + new_binary = f"{destination}/{os.path.basename(ref_binary)}" + new_config = f"{destination}/new_config.json" + config_data[config_member] = new_binary + with open(new_config, "w") as f: + json.dump(config_data, f, indent=2) + return ref_binary, new_binary, new_config + + +def test_nxpimage_cert_block_get_template(tmpdir): + out_file = f"{tmpdir}/cert_block_template.yaml" + runner = CliRunner() + cmd = f"cert-block get-template {out_file}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(out_file) + + +def test_nxpimage_cert_block_parse(elftosb_data_dir, tmpdir): + out_folder = tmpdir + input_file = os.path.join( + elftosb_data_dir, "workspace", "output_images", "lpc55s3x", "cert_384_256.bin" + ) + runner = CliRunner() + cmd = f"cert-block parse -b {input_file} {out_folder}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(os.path.join(out_folder, "cert_block_config.yaml")) + assert os.path.isfile(os.path.join(out_folder, "rootCertificate0File.pub")) + assert os.path.isfile(os.path.join(out_folder, "signingCertificateFile.pub")) diff --git a/tests/nxpimage/test_nxpimage_cfg_template.py b/tests/nxpimage/test_nxpimage_cfg_template.py index 37085e07..1c7e3275 100644 --- a/tests/nxpimage/test_nxpimage_cfg_template.py +++ b/tests/nxpimage/test_nxpimage_cfg_template.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020-2022 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -30,7 +30,7 @@ def test_nxpimage_cfgtmp_create_sb3(tmpdir, device): @pytest.mark.parametrize( "device", - [("rt1180")], + [("rt118x")], ) def test_nxpimage_cfgtmp_create_ahab(tmpdir, device): runner = CliRunner() diff --git a/tests/nxpimage/test_nxpimage_fcb.py b/tests/nxpimage/test_nxpimage_fcb.py index 22944bc0..3e7f0637 100644 --- a/tests/nxpimage/test_nxpimage_fcb.py +++ b/tests/nxpimage/test_nxpimage_fcb.py @@ -27,7 +27,7 @@ ("lpc55s3x", "flexspi_nor"), ], ) -def test_nxpimage_bimg_export(tmpdir, data_dir, family, mem_type): +def test_nxpimage_fcb_export(tmpdir, data_dir, family, mem_type): runner = CliRunner() with use_working_directory(data_dir): config_file = os.path.join(data_dir, "fcb", family, f"fcb_{family}_{mem_type}.yaml") @@ -83,5 +83,5 @@ def test_nxpimage_fcb_template_cli(tmpdir, family, mem_types): assert result.exit_code == 0 for mem_type in mem_types: - template_name = os.path.join(tmpdir, f"fcb_{family}_{mem_type}.yml") + template_name = os.path.join(tmpdir, f"fcb_{family}_{mem_type}.yaml") assert os.path.isfile(template_name) diff --git a/tests/nxpimage/test_nxpimage_hab.py b/tests/nxpimage/test_nxpimage_hab.py new file mode 100644 index 00000000..ec7619c3 --- /dev/null +++ b/tests/nxpimage/test_nxpimage_hab.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +"""Test BEE part of nxpimage app.""" +import filecmp +import os +import shutil + +import pytest +from click.testing import CliRunner + +from spsdk.apps import nxpimage +from spsdk.utils.misc import load_binary, load_configuration, use_working_directory + + +@pytest.mark.parametrize( + "command_file, externals, ref_file", + [ + ( + "evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bd", + ["evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.s19"], + "evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bin", + ), + ( + "evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.bd", + ["evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.s19"], + "evkmimxrt1170_iled_blinky_cm7_QSPI_FLASH_unsigned.bin", + ), + ( + "evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bd", + ["evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.srec"], + "evkbimxrt1160_iled_blinky_cm7_xip_mdk_unsigned.bin", + ), + ( + "evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd", + ["evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19"], + "evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bin", + ), + ( + "evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bd", + ["evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.s19"], + "evkmimxrt1170_iled_blinky_cm7_int_RAM_non_xip_unsigned.bin", + ), + ( + "evkmimxrt1170_flashloader.bd", + ["evkmimxrt1170_flashloader.srec"], + "evkmimxrt1170_flashloader.bin", + ), + ], +) +def test_nxpimage_hab_export(tmpdir, data_dir, command_file, externals, ref_file): + test_data_dir = os.path.join(data_dir, "hab") + command_file_path = os.path.join(test_data_dir, command_file) + runner = CliRunner() + with use_working_directory(tmpdir): + output_file_path = os.path.join(tmpdir, "image_output.bin") + cmd = f"hab export --command {command_file_path} --output {output_file_path}" + for external in externals: + ext_path = os.path.join(test_data_dir, external) + cmd = f"{cmd} {ext_path}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(output_file_path) + + ref_file_path = os.path.join(test_data_dir, ref_file) + encrypted_image_enc = load_binary(ref_file_path) + encrypted_nxpimage = load_binary(output_file_path) + assert encrypted_image_enc == encrypted_nxpimage + + +@pytest.mark.parametrize( + "source_bin, segments", + [ + ("evkmimxrt1170_iled_blinky_cm7_int_RAM_unsigned.bin", ["ivt", "bdt", "app"]), + ], +) +def test_nxpimage_hab_parse(tmpdir, data_dir, source_bin: str, segments): + test_data_dir = os.path.join(data_dir, "hab") + source_bin_path = os.path.join(test_data_dir, source_bin) + runner = CliRunner() + with use_working_directory(tmpdir): + cmd = f"hab parse --binary {source_bin_path} {tmpdir}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + ref_output_dir = os.path.join(test_data_dir, source_bin.split(".")[0]) + for segment in segments: + segment_file_name = f"{segment}.bin" + segment_file_path = os.path.join(tmpdir, segment_file_name) + assert os.path.isfile(segment_file_path) + assert filecmp.cmp( + os.path.join(ref_output_dir, segment_file_name), + segment_file_path, + shallow=False, + ) diff --git a/tests/nxpimage/test_nxpimage_iee.py b/tests/nxpimage/test_nxpimage_iee.py index 0d5001f0..1bf10008 100644 --- a/tests/nxpimage/test_nxpimage_iee.py +++ b/tests/nxpimage/test_nxpimage_iee.py @@ -50,6 +50,12 @@ "encrypted_blobs.bin", "iee_keyblob.bin", ), + ( + "aes_xts512_rt1180", + "iee_config.yaml", + "encrypted_blob.bin", + None, + ), ], ) def test_nxpimage_iee(tmpdir, data_dir, case, config, reference, keyblobs): @@ -68,7 +74,6 @@ def test_nxpimage_iee(tmpdir, data_dir, case, config, reference, keyblobs): result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 assert os.path.isfile(os.path.join(out_dir, output_name)) - assert os.path.isfile(os.path.join(out_dir, keyblob_name)) assert os.path.isfile(os.path.join(out_dir, encrypted_name)) if reference: @@ -77,6 +82,7 @@ def test_nxpimage_iee(tmpdir, data_dir, case, config, reference, keyblobs): assert encrypted_image_enc == encrypted_nxpimage if keyblobs: + assert os.path.isfile(os.path.join(out_dir, keyblob_name)) reference_keyblob = load_binary(keyblobs) keyblobs_nxpimage = load_binary(os.path.join(out_dir, keyblob_name)) assert reference_keyblob == keyblobs_nxpimage @@ -84,7 +90,7 @@ def test_nxpimage_iee(tmpdir, data_dir, case, config, reference, keyblobs): def test_nxpimage_iee_template_cli(tmpdir): runner = CliRunner() - template = os.path.join(tmpdir, "bee_template.yaml") + template = os.path.join(tmpdir, "iee_template.yaml") cmd = f"iee get-template {template}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 diff --git a/tests/nxpimage/test_nxpimage_mbi.py b/tests/nxpimage/test_nxpimage_mbi.py index 161611de..4fe0d737 100644 --- a/tests/nxpimage/test_nxpimage_mbi.py +++ b/tests/nxpimage/test_nxpimage_mbi.py @@ -9,9 +9,11 @@ import filecmp import json import os +import shutil import commentjson as json import pytest +import yaml from click.testing import CliRunner from spsdk import SPSDKError @@ -19,9 +21,9 @@ from spsdk.image.exceptions import SPSDKUnsupportedImageType from spsdk.image.keystore import KeyStore from spsdk.image.mbi_mixin import Mbi_MixinHmac -from spsdk.image.mbimg import Mbi_PlainRamLpc55s3x, Mbi_PlainXipSignedLpc55s3x +from spsdk.image.mbimg import DEVICE_FILE, Mbi_PlainRamLpc55s3x, Mbi_PlainXipSignedLpc55s3x from spsdk.utils.crypto.backend_internal import ECC, RSA, internal_backend -from spsdk.utils.misc import use_working_directory +from spsdk.utils.misc import load_configuration, use_working_directory def process_config_file(config_path: str, destination: str): @@ -30,10 +32,12 @@ def process_config_file(config_path: str, destination: str): for key in config_data: if isinstance(config_data[key], str): config_data[key] = config_data[key].replace("\\", "/") - ref_binary = config_data["masterBootOutputFile"] + ref_binary = config_data.get("masterBootOutputFile") or config_data.get("containerOutputFile") new_binary = f"{destination}/{os.path.basename(ref_binary)}" - new_config = f"{destination}/new_config.json" + new_config = f"{destination}/{os.path.basename(config_path)}" config_data["masterBootOutputFile"] = new_binary + # It doesn't matter that there will be both keys in this temporary config + config_data["containerOutputFile"] = new_binary with open(new_config, "w") as f: json.dump(config_data, f, indent=2) return ref_binary, new_binary, new_config @@ -44,7 +48,7 @@ def get_signing_key(config_file) -> ECC.EccKey: config_data = json.load(f) private_key_file = ( config_data["signingCertificatePrivateKeyFile"] - if config_data["useIsk"] + if config_data.get("useIsk", True) or "" in config_data else config_data["mainRootCertPrivateKeyFile"] ) with open(private_key_file.replace("\\", "/"), "rb") as f: @@ -118,6 +122,7 @@ def test_nxpimage_mbi_signed(elftosb_data_dir, tmpdir, config_file, device, sign cmd = f"mbi export {new_config}" result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 assert os.path.isfile(new_binary) # validate file lengths @@ -168,6 +173,105 @@ def test_nxpimage_mbi_signed(elftosb_data_dir, tmpdir, config_file, device, sign assert ref_data[:-signature_length] == new_data[:-signature_length] +@pytest.mark.parametrize( + "mbi_config_file,cert_block_config_file,device", + [ + ("mb_xip_384_256_cert.json", "cert_384_256.json", "lpc55s3x"), + ("mb_xip_384_384_cert.json", "cert_384_384.json", "lpc55s3x"), + ], +) +def test_nxpimage_mbi_cert_block_signed( + elftosb_data_dir, tmpdir, mbi_config_file, cert_block_config_file, device +): + runner = CliRunner() + with use_working_directory(elftosb_data_dir): + cert_config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{cert_block_config_file}" + mbi_config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{mbi_config_file}" + mbi_ref_binary, mbi_new_binary, mbi_new_config = process_config_file( + mbi_config_file, tmpdir + ) + cert_ref_binary, cert_new_binary, cert_new_config = process_config_file( + cert_config_file, tmpdir + ) + + cmd = f"cert-block export {cert_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(cert_new_binary) + + # validate cert file file lengths + with open(cert_ref_binary, "rb") as f: + cert_ref_data = f.read() + with open(cert_new_binary, "rb") as f: + cert_new_data = f.read() + assert len(cert_ref_data) == len(cert_new_data) + assert cert_ref_data == cert_new_data + + cmd = f"mbi export {mbi_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(mbi_new_binary) + + # validate file lengths + with open(mbi_ref_binary, "rb") as f: + mbi_ref_data = f.read() + with open(mbi_new_binary, "rb") as f: + mbi_new_data = f.read() + assert len(mbi_ref_data) == len(mbi_new_data) + + # validate signatures + + signing_key = get_signing_key(config_file=mbi_config_file) + signature_length = 2 * signing_key.pointQ.size_in_bytes() + + assert internal_backend.ecc_verify( + signing_key, mbi_new_data[-signature_length:], mbi_new_data[:-signature_length] + ) + assert internal_backend.ecc_verify( + signing_key, mbi_ref_data[-signature_length:], mbi_ref_data[:-signature_length] + ) + # validate data before signature + assert mbi_ref_data[:-signature_length] == mbi_new_data[:-signature_length] + + +@pytest.mark.parametrize( + "mbi_config_file,cert_block_config_file,device", + [ + ("mb_xip_384_256_cert_invalid.json", "cert_384_256.json", "lpc55s3x"), + ], +) +def test_nxpimage_mbi_cert_block_signed_invalid( + elftosb_data_dir, tmpdir, mbi_config_file, cert_block_config_file, device +): + runner = CliRunner() + with use_working_directory(elftosb_data_dir): + cert_config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{cert_block_config_file}" + mbi_config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{mbi_config_file}" + mbi_ref_binary, mbi_new_binary, mbi_new_config = process_config_file( + mbi_config_file, tmpdir + ) + cert_ref_binary, cert_new_binary, cert_new_config = process_config_file( + cert_config_file, tmpdir + ) + + cmd = f"cert-block export {cert_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(cert_new_binary) + + # validate cert file file lengths + with open(cert_ref_binary, "rb") as f: + cert_ref_data = f.read() + with open(cert_new_binary, "rb") as f: + cert_new_data = f.read() + assert len(cert_ref_data) == len(cert_new_data) + assert cert_ref_data == cert_new_data + + cmd = f"mbi export {mbi_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code != 0 + + # skip_hmac_keystore # 0 indicates no hmac and no keystore present in output image # 1 indicates hmac present but no keystore @@ -275,6 +379,7 @@ def test_nxpimage_mbi_legacy_encrypted( cmd = f"mbi export {new_config}" result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 assert os.path.isfile(new_binary) # validate file lengths @@ -349,3 +454,192 @@ def test_mbi_lpc55s3x_invalid(): with pytest.raises(SPSDKError): mbi.validate() + + +@pytest.mark.parametrize( + "family", + [ + "lpc55xx", + "lpc55s0x", + "lpc550x", + "lpc55s1x", + "lpc551x", + "lpc55s2x", + "lpc552x", + "lpc55s6x", + "nhs52sxx", + "rt5xx", + "rt6xx", + "lpc55s3x", + "kw45xx", + "k32w1xx", + "lpc553x", + ], +) +def test_mbi_get_templates(tmpdir, family): + runner = CliRunner() + cmd = f"mbi get-templates -f {family} {tmpdir}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + device_data = load_configuration(path=DEVICE_FILE) + images = device_data["devices"][family]["images"] + for image in images: + for config in images[image]: + file_path = os.path.join(tmpdir, f"{family}_{image}_{config}.yaml") + assert os.path.isfile(file_path) + + +@pytest.mark.parametrize( + "family, template_name, keys_to_copy", + [ + ( + "lpc55s3x", + "ext_xip_signed_lpc55s3x.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "lpc553x", + "ext_xip_signed_lpc55s3x.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "rt5xx", + "ext_xip_signed_rtxxxx.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "rt6xx", + "ext_xip_signed_rtxxxx.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc550x", + "int_xip_signed_xip.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc551x", + "int_xip_signed_xip.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc55s2x", + "int_xip_signed_xip.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "nhs52sxx", + "int_xip_signed_xip.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "kw45xx", + "int_xip_signed_kw45xx.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "k32w1xx", + "int_xip_signed_kw45xx.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ], +) +def test_mbi_export_sign_provider(tmpdir, data_dir, family, template_name, keys_to_copy): + mbi_data_dir = os.path.join(data_dir, "mbi") + config_path = os.path.join(mbi_data_dir, template_name) + config = load_configuration(config_path) + config["family"] = family + + for key_file_name in keys_to_copy: + key_file_path = os.path.join(mbi_data_dir, "keys_and_certs", key_file_name) + shutil.copyfile(key_file_path, os.path.join(tmpdir, key_file_name)) + test_app_path = os.path.join(mbi_data_dir, config["inputImageFile"]) + shutil.copyfile(test_app_path, os.path.join(tmpdir, config["inputImageFile"])) + tmp_config = os.path.join(tmpdir, "config.yaml") + with open(tmp_config, "w") as file: + yaml.dump(config, file) + + runner = CliRunner() + cmd = f"mbi export {tmp_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + file_path = os.path.join(tmpdir, config["masterBootOutputFile"]) + assert os.path.isfile(file_path) + + +@pytest.mark.parametrize( + "family, template_name, keys_to_copy", + [ + ( + "lpc55s3x", + "ext_xip_signed_lpc55s3x_invalid.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "lpc553x", + "ext_xip_signed_lpc55s3x_invalid.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "rt5xx", + "ext_xip_signed_rtxxxx_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "rt6xx", + "ext_xip_signed_rtxxxx_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc550x", + "int_xip_signed_xip_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc551x", + "int_xip_signed_xip_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "lpc55s2x", + "int_xip_signed_xip_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "nhs52sxx", + "int_xip_signed_xip_invalid.yml", + ["k0_cert0_2048.pem", "root_k0_signed_cert0_noca.der.cert"], + ), + ( + "kw45xx", + "int_xip_signed_kw45xx_invalid.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ( + "k32w1xx", + "int_xip_signed_kw45xx_invalid.yml", + ["ec_pk_secp256r1_cert0.pem", "ec_secp256r1_cert0.pem"], + ), + ], +) +def test_mbi_export_sign_provider_invalid_configuration( + tmpdir, data_dir, family, template_name, keys_to_copy +): + mbi_data_dir = os.path.join(data_dir, "mbi") + config_path = os.path.join(mbi_data_dir, template_name) + config = load_configuration(config_path) + config["family"] = family + + for key_file_name in keys_to_copy: + key_file_path = os.path.join(mbi_data_dir, "keys_and_certs", key_file_name) + shutil.copyfile(key_file_path, os.path.join(tmpdir, key_file_name)) + test_app_path = os.path.join(mbi_data_dir, config["inputImageFile"]) + shutil.copyfile(test_app_path, os.path.join(tmpdir, config["inputImageFile"])) + tmp_config = os.path.join(tmpdir, "config.yaml") + with open(tmp_config, "w") as file: + yaml.dump(config, file) + + runner = CliRunner() + cmd = f"mbi export {tmp_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code != 0 diff --git a/tests/nxpimage/test_nxpimage_otfad.py b/tests/nxpimage/test_nxpimage_otfad.py index 50135452..cee4a7ff 100644 --- a/tests/nxpimage/test_nxpimage_otfad.py +++ b/tests/nxpimage/test_nxpimage_otfad.py @@ -43,8 +43,34 @@ def test_nxpimage_otfad_export(tmpdir, data_dir, config): @pytest.mark.parametrize( - "config,per_ix,blhost_bcf_res,bin_out", + "config,per_ix,blhost_bcf_res,bin_out,family", [ + ( + "otfad_rt1160.yaml", + 2, + [ + "efuse-program-once 128 0xffeeddcc --no-verify", + "efuse-program-once 129 0xbbaa9988 --no-verify", + "efuse-program-once 130 0x77665544 --no-verify", + "efuse-program-once 131 0x33221100 --no-verify", + "efuse-program-once 71 0x00000000 --no-verify", + ], + "otfad_rt1160_out.bin", + "rt116x", + ), + ( + "otfad_rt1170.yaml", + 2, + [ + "efuse-program-once 128 0xffeeddcc --no-verify", + "efuse-program-once 129 0xbbaa9988 --no-verify", + "efuse-program-once 130 0x77665544 --no-verify", + "efuse-program-once 131 0x33221100 --no-verify", + "efuse-program-once 71 0x00000000 --no-verify", + ], + "otfad_rt1170_out.bin", + "rt117x", + ), ( "otfad_rt1180.yaml", 2, @@ -53,6 +79,7 @@ def test_nxpimage_otfad_export(tmpdir, data_dir, config): "efuse-program-once 182 0x00000008 --no-verify", ], "otfad_rt1180_out.bin", + "rt118x", ), ( "otfad_rt1180_txt.yaml", @@ -62,6 +89,7 @@ def test_nxpimage_otfad_export(tmpdir, data_dir, config): "efuse-program-once 182 0x00000008 --no-verify", ], "otfad_rt1180_out.bin", + "rt118x", ), ( "otfad_rt1180_scramble.yaml", @@ -72,10 +100,13 @@ def test_nxpimage_otfad_export(tmpdir, data_dir, config): "efuse-program-once 183 0x78563412 --no-verify", ], "otfad_rt1180_scramble_out.bin", + "rt118x", ), ], ) -def test_nxpimage_otfad_export_rt1180(tmpdir, data_dir, config, per_ix, blhost_bcf_res, bin_out): +def test_nxpimage_otfad_export_rt11x0( + tmpdir, data_dir, config, per_ix, blhost_bcf_res, bin_out, family +): runner = CliRunner() work_dir = os.path.join(tmpdir, "otfad") shutil.copytree(os.path.join(data_dir, "otfad"), work_dir) @@ -85,13 +116,13 @@ def test_nxpimage_otfad_export_rt1180(tmpdir, data_dir, config, per_ix, blhost_b cmd = f"otfad export -i {per_ix} {config}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 - assert os.path.isfile(os.path.join(out_dir, f"otfad{per_ix}_rt1180_blhost.bcf")) + assert os.path.isfile(os.path.join(out_dir, f"otfad{per_ix}_{family}_blhost.bcf")) assert os.path.isfile(os.path.join(out_dir, "encrypted_blobs.bin")) assert os.path.isfile(os.path.join(out_dir, "OTFAD_Table.bin")) assert os.path.isfile(os.path.join(out_dir, "otfad_whole_image.bin")) assert os.path.isfile(os.path.join(out_dir, "readme.txt")) - blhost_script = load_text(f"otfad{per_ix}_rt1180_blhost.bcf", search_paths=[out_dir]) + blhost_script = load_text(f"otfad{per_ix}_{family}_blhost.bcf", search_paths=[out_dir]) for result in blhost_bcf_res: assert result in blhost_script diff --git a/tests/nxpimage/test_nxpimage_sb31.py b/tests/nxpimage/test_nxpimage_sb31.py index 3e9f3181..99a5e0b2 100644 --- a/tests/nxpimage/test_nxpimage_sb31.py +++ b/tests/nxpimage/test_nxpimage_sb31.py @@ -1,16 +1,15 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause """Test SecureBinary part of nxpimage app.""" import filecmp import json -import logging import os -from typing import Tuple +from typing import Dict, Union import pytest from click.testing import CliRunner @@ -19,18 +18,18 @@ from spsdk.utils.misc import use_working_directory -def process_config_file( - config_path: str, destination: str, config_member: str -) -> Tuple[str, str, str]: +def process_config_file(config_path: str, destination: str): with open(config_path) as f: - config_data = json.load(f) + config_data: Dict[str, Union[str, int]] = json.load(f) for key in config_data: if isinstance(config_data[key], str): config_data[key] = config_data[key].replace("\\", "/") - ref_binary = config_data[config_member] + ref_binary = config_data.get("containerOutputFile") or config_data.get("containerOutputFile") new_binary = f"{destination}/{os.path.basename(ref_binary)}" - new_config = f"{destination}/new_config.json" - config_data[config_member] = new_binary + new_config = f"{destination}/{os.path.basename(config_path)}" + config_data["containerOutputFile"] = new_binary + # It doesn't matter that there will be both keys in this temporary config + config_data["containerOutputFile"] = new_binary with open(new_config, "w") as f: json.dump(config_data, f, indent=2) return ref_binary, new_binary, new_config @@ -54,9 +53,7 @@ def test_nxpimage_sb31(elftosb_data_dir, tmpdir, config_file, device): runner = CliRunner() with use_working_directory(elftosb_data_dir): config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{config_file}" - ref_binary, new_binary, new_config = process_config_file( - config_file, tmpdir, "containerOutputFile" - ) + ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) cmd = f"sb31 export {new_config}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 @@ -64,16 +61,47 @@ def test_nxpimage_sb31(elftosb_data_dir, tmpdir, config_file, device): assert filecmp.cmp(ref_binary, new_binary, shallow=False) -def test_nxpimage_sb31_notime(elftosb_data_dir, tmpdir): +@pytest.mark.parametrize( + "sb31_cfg,cert_block_cfg,device", + [ + ("sb3_256_256.json", "cert_256_256.json", "lpc55s3x"), + ("sb3_384_256.json", "cert_384_256.json", "lpc55s3x"), + ("sb3_384_384.json", "cert_384_384.json", "lpc55s3x"), + ], +) +def test_nxpimage_sb31_cert_block(elftosb_data_dir, tmpdir, sb31_cfg, cert_block_cfg, device): + runner = CliRunner() + with use_working_directory(elftosb_data_dir): + cert_cfg_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{cert_block_cfg}" + sb31_cfg_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{sb31_cfg}" + cert_ref_binary, cert_new_binary, cert_new_config = process_config_file( + cert_cfg_file, tmpdir + ) + sb31_ref_binary, sb31_new_binary, sb31_new_config = process_config_file( + sb31_cfg_file, tmpdir + ) + # Generate and verify certification block + cmd = f"cert-block export {cert_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(cert_new_binary) + assert filecmp.cmp(cert_ref_binary, cert_new_binary, shallow=False) + # Generate and verify SB31 with certification block + cmd = f"sb31 export {sb31_new_config}" + result = runner.invoke(nxpimage.main, cmd.split()) + assert result.exit_code == 0 + assert os.path.isfile(sb31_new_binary) + assert filecmp.cmp(sb31_ref_binary, sb31_new_binary, shallow=False) + + +def test_nxpimage_sb31_notime(elftosb_data_dir, tmpdir): config_file = "sb3_256_256.json" device = "lpc55s3x" runner = CliRunner() with use_working_directory(elftosb_data_dir): config_file = f"{elftosb_data_dir}/workspace/cfgs/{device}/{config_file}" - ref_binary, new_binary, new_config = process_config_file( - config_file, tmpdir, "containerOutputFile" - ) + ref_binary, new_binary, new_config = process_config_file(config_file, tmpdir) cmd = f"sb31 export {new_config}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 diff --git a/tests/nxpimage/test_nxpimage_xmcd.py b/tests/nxpimage/test_nxpimage_xmcd.py index 5904c9ac..c54cc340 100644 --- a/tests/nxpimage/test_nxpimage_xmcd.py +++ b/tests/nxpimage/test_nxpimage_xmcd.py @@ -10,6 +10,7 @@ import os import pytest +import yaml from click.testing import CliRunner from spsdk.apps import nxpimage @@ -18,49 +19,65 @@ @pytest.mark.parametrize( - "family,mem_type,config_type", + "family,mem_type,config_type,option", [ - ("rt117x", "semc_sdram", "simplified"), - ("rt117x", "semc_sdram", "full"), - ("rt117x", "flexspi_ram", "simplified"), - ("rt116x", "semc_sdram", "simplified"), - ("rt116x", "semc_sdram", "full"), - ("rt116x", "flexspi_ram", "simplified"), + ("rt117x", "semc_sdram", "simplified", None), + ("rt117x", "semc_sdram", "full", None), + ("rt117x", "flexspi_ram", "simplified", 0), + ("rt117x", "flexspi_ram", "simplified", 1), + ("rt117x", "flexspi_ram", "full", None), + ("rt116x", "semc_sdram", "simplified", None), + ("rt116x", "semc_sdram", "full", None), + ("rt116x", "flexspi_ram", "simplified", 0), + ("rt116x", "flexspi_ram", "simplified", 1), + ("rt116x", "flexspi_ram", "full", None), ], ) -def test_nxpimage_xmcd_export(tmpdir, data_dir, family, mem_type, config_type): +def test_nxpimage_xmcd_export(tmpdir, data_dir, family, mem_type, config_type, option): runner = CliRunner() with use_working_directory(data_dir): - config_file = os.path.join(data_dir, "xmcd", family, f"{mem_type}_{config_type}.yaml") + file_base_name = f"{mem_type}_{config_type}" + if option is not None: + file_base_name += f"_{option}" + config_file_path = os.path.join(data_dir, "xmcd", family, f"{file_base_name}.yaml") out_file = os.path.join(tmpdir, f"xmcd_{family}_{mem_type}_{config_type}_exported.bin") - cmd = f"bootable-image xmcd export -c {config_file} {out_file}" + cmd = f"bootable-image xmcd export -c {config_file_path} {out_file}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 assert os.path.isfile(out_file) assert filecmp.cmp( - os.path.join(data_dir, "xmcd", family, f"{mem_type}_{config_type}.bin"), + os.path.join(data_dir, "xmcd", family, f"{file_base_name}.bin"), out_file, shallow=False, ) @pytest.mark.parametrize( - "family,mem_type,config_type", + "family,mem_type,config_type,option", [ - ("rt117x", "semc_sdram", "simplified"), - ("rt117x", "semc_sdram", "full"), - ("rt117x", "flexspi_ram", "simplified"), - ("rt116x", "semc_sdram", "simplified"), - ("rt116x", "semc_sdram", "full"), - ("rt116x", "flexspi_ram", "simplified"), + ("rt117x", "semc_sdram", "simplified", None), + ("rt117x", "semc_sdram", "full", None), + ("rt117x", "flexspi_ram", "simplified", 0), + ("rt117x", "flexspi_ram", "simplified", 1), + ("rt117x", "flexspi_ram", "full", None), + ("rt116x", "semc_sdram", "simplified", None), + ("rt116x", "semc_sdram", "full", None), + ("rt116x", "flexspi_ram", "simplified", 0), + ("rt116x", "flexspi_ram", "simplified", 1), + ("rt116x", "flexspi_ram", "full", None), ], ) -def test_nxpimage_xmcd_parse_cli(tmpdir, data_dir, family, mem_type, config_type): +def test_nxpimage_xmcd_parse_cli(tmpdir, data_dir, family, mem_type, config_type, option): runner = CliRunner() with use_working_directory(data_dir): data_folder = os.path.join(data_dir, "xmcd", family) output_file = os.path.join(tmpdir, f"xmcd_{family}_{mem_type}_{config_type}.yaml") - cmd = f"bootable-image xmcd parse -f {family} -b {data_folder}/{mem_type}_{config_type}.bin {output_file}" + file_base_name = f"{mem_type}_{config_type}" + if option is not None: + file_base_name += f"_{option}" + bin_path = os.path.join(data_folder, f"{file_base_name}.bin") + + cmd = f"bootable-image xmcd parse -f {family} -b {bin_path} {output_file}" result = runner.invoke(nxpimage.main, cmd.split()) assert result.exit_code == 0 @@ -71,7 +88,8 @@ def test_nxpimage_xmcd_parse_cli(tmpdir, data_dir, family, mem_type, config_type "family", ["rt117x", "rt116x"], ) -def test_nxpimage_xmcd_template_cli(tmpdir, family): +def test_nxpimage_xmcd_template_cli(tmpdir, data_dir, family): + templates_folder = os.path.join(data_dir, "xmcd", family, "templates") runner = CliRunner() cmd = f"bootable-image xmcd get-templates -f {family} {tmpdir}" result = runner.invoke(nxpimage.main, cmd.split()) @@ -81,5 +99,12 @@ def test_nxpimage_xmcd_template_cli(tmpdir, family): for mem_type in mem_types: config_types = XMCD.get_supported_configuration_types(family, mem_type) for config_type in config_types: - template_name = os.path.join(tmpdir, f"xmcd_{family}_{mem_type}_{config_type}.yml") - assert os.path.isfile(template_name) + template_name = f"xmcd_{family}_{mem_type}_{config_type}.yaml" + new_template_path = os.path.join(tmpdir, template_name) + assert os.path.isfile(new_template_path) + with open(new_template_path) as f: + new_template = yaml.safe_load(f) + ref_template_path = os.path.join(templates_folder, template_name) + with open(ref_template_path) as f: + ref_template = yaml.safe_load(f) + assert new_template == ref_template diff --git a/tests/pfr/data/bad_dev.yml b/tests/pfr/data/bad_dev.yml index 4d88e7d1..501f0bfd 100644 --- a/tests/pfr/data/bad_dev.yml +++ b/tests/pfr/data/bad_dev.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP LPC55S6X PFR CMPA configuration description: # The PFR CMPA configuration description. device: invalid # The NXP device name. diff --git a/tests/pfr/data/bad_rev.yml b/tests/pfr/data/bad_rev.yml index b937b229..4dfd638d 100644 --- a/tests/pfr/data/bad_rev.yml +++ b/tests/pfr/data/bad_rev.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP LPC55S6X PFR CMPA configuration description: # The PFR CMPA configuration description. device: lpc55s6x # The NXP device name. diff --git a/tests/pfr/data/cfpa-lpc551x.yaml b/tests/pfr/data/cfpa-lpc551x.yaml index f92b52b1..10940e2c 100644 --- a/tests/pfr/data/cfpa-lpc551x.yaml +++ b/tests/pfr/data/cfpa-lpc551x.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc551x PFR CFPA configuration description: # The PFR CFPA configuration description. device: lpc551x # The NXP device name. diff --git a/tests/pfr/data/cfpa_after_reset.yml b/tests/pfr/data/cfpa_after_reset.yml index d90b486f..fa765539 100644 --- a/tests/pfr/data/cfpa_after_reset.yml +++ b/tests/pfr/data/cfpa_after_reset.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s6x PFR CFPA configuration description: # The PFR CFPA configuration description. device: lpc55s6x # The NXP device name. diff --git a/tests/pfr/data/cfpa_lpc55s3x_default.yaml b/tests/pfr/data/cfpa_lpc55s3x_default.yaml index a09f8dd5..07fc8585 100644 --- a/tests/pfr/data/cfpa_lpc55s3x_default.yaml +++ b/tests/pfr/data/cfpa_lpc55s3x_default.yaml @@ -1,15 +1,17 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s3x PFR CFPA configuration -description: # The PFR CFPA configuration description. +description: # The CFPA configuration description. device: lpc55s3x # The NXP device name. revision: 1a # The NXP device revision. - type: CFPA # The PFR type (CMPA, CFPA). - version: 1.7.1 # The SPSDK tool version. - author: NXP # The author of the configuration. - release: alpha # The SPSDK release. -settings: # The PFR CFPA registers configuration. + type: CFPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.1 # The SPSDK tool version. +settings: # The CFPA registers configuration. HEADER: # Header value: '0x00000000' # The value width: 32b - CFPA_PAGE_VERSION: # CFPA Page Version + CFPA_PAGE_VERSION: # CFPA Page Version. Use value of 0xFFFF_FFFF to auto increment current CFPA page version value (by ROM). value: '0x00000000' # The value width: 32b S_FW_Version: # Secure Firmware Version (Monotonic counter) value: '0x00000000' # The value width: 32b @@ -49,107 +51,101 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b ROTKH_REVOKE: # Root of Trust Key Hash Revoke bitfields: # The register bitfields - RoTK0_EN: ROTKH_REVOKE_RoTK0_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 0 Enable - # - ROTKH_REVOKE_RoTK0_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK0_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK0_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK0_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK1_EN: ROTKH_REVOKE_RoTK1_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 1 Enable - # - ROTKH_REVOKE_RoTK1_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK1_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK1_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK1_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK2_EN: ROTKH_REVOKE_RoTK2_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 2 Enable - # - ROTKH_REVOKE_RoTK2_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK2_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK2_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK2_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK3_EN: ROTKH_REVOKE_RoTK3_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 3 Enable - # - ROTKH_REVOKE_RoTK3_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK3_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK3_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK3_EN_KEY_REVOKED_1, (3): Key Revoked - DICE_SKIP_CSR: ROTKH_REVOKE_DICE_SKIP_CSR_GENERATE_CSR # Width: 2b[0-3], Description: DICE Skip CSR - # - ROTKH_REVOKE_DICE_SKIP_CSR_GENERATE_CSR, (0): Generate CSR - # - ROTKH_REVOKE_DICE_SKIP_CSR_SKIP_CSR, (1): Skip CSR + RoTK0_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 0 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK1_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 1 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK2_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 2 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK3_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 3 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + DICE_SKIP_CSR: GENERATE_CSR # Width: 2b, Description: DICE Skip CSR + # - GENERATE_CSR, (0): Generate CSR + # - SKIP_CSR, (1): Skip CSR VENDOR_USAGE: # Vendor Usage bitfields: # The register bitfields - DBG_VENDOR_USAGE: 0 # Width: 16b[0-65535], Description: Debug Vendor Usage + DBG_VENDOR_USAGE: '0x0000' # Width: 16b, Description: Debug Vendor Usage DCFG_CC_SOCU_NS_PIN: # Device Configuration Credential Constraints for SoC specific Use Pinned bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP # Width: 1b[0-1], Description: Non-secure Non-invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_FIXED_STATE, (1): Fixed state - DBGEN: DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP # Width: 1b[0-1], Description: Non-secure Debug Enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_FIXED_STATE, (1): Fixed state - SPNIDEN: DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP # Width: 1b[0-1], Description: Secure Non-invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_FIXED_STATE, (1): Fixed state - SPIDEN: DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP # Width: 1b[0-1], Description: Secure Invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_FIXED_STATE, (1): Fixed state - DSP_DBGEN: DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_USE_DAP # Width: 1b[0-1], Description: DSP Debug Enable - # - DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_FIXED_STATE, (1): Fixed state - ISP_CMD_EN: DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP # Width: 1b[0-1], Description: ISP Boot Command Enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_FIXED_STATE, (1): Fixed state - FA_CMD_EN: DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_USE_DAP # Width: 1b[0-1], Description: FA Command Enable - # - DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_FIXED_STATE, (1): Fixed state - ME_CMD_EN: DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_USE_DAP # Width: 1b[0-1], Description: Flash Mass Erase Command Enable - # - DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_FIXED_STATE, (1): Fixed state - UUID_CHECK: DCFG_CC_SOCU_NS_PIN_UUID_CHECK_DISABLED # Width: 1b[0-1], Description: UUID Check - # - DCFG_CC_SOCU_NS_PIN_UUID_CHECK_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_PIN_UUID_CHECK_ENABLED, (1): Enabled + NIDEN: USE_DAP # Width: 1b, Description: Non-secure Non-invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DBGEN: USE_DAP # Width: 1b, Description: Non-secure Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPNIDEN: USE_DAP # Width: 1b, Description: Secure Non-invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPIDEN: USE_DAP # Width: 1b, Description: Secure Invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ISP_CMD_EN: USE_DAP # Width: 1b, Description: ISP Boot Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + FA_CMD_EN: USE_DAP # Width: 1b, Description: FA Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ME_CMD_EN: USE_DAP # Width: 1b, Description: Flash Mass Erase Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + UUID_CHECK: DISABLED # Width: 1b, Description: Enforce UUID match during debug authentication. + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled DCFG_CC_SOCU_NS_DFLT: # Device Configuration Credential Constraints for SoC specific Use Debug Filter bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED # Width: 1b[0-1], Description: Non-secure Non-invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_ENABLED, (1): Enabled - DBGEN: DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED # Width: 1b[0-1], Description: Non-secure Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_ENABLED, (1): Enabled - SPNIDEN: DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED # Width: 1b[0-1], Description: Secure Non-invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_ENABLED, (1): Enabled - SPIDEN: DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED # Width: 1b[0-1], Description: Secure Invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_ENABLED, (1): Enabled - DSP_DBGEN: DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_DISABLED # Width: 1b[0-1], Description: DSP Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_ENABLED, (1): Enabled - ISP_CMD_EN: DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED # Width: 1b[0-1], Description: ISP Boot Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_ENABLED, (1): Enabled - FA_CMD_EN: DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_DISABLED # Width: 1b[0-1], Description: FA Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_ENABLED, (1): Enabled - ME_CMD_EN: DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_DISABLED # Width: 1b[0-1], Description: Flash Mass Erase Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_ENABLED, (1): Enabled + NIDEN: DISABLED # Width: 1b, Description: Non-secure Non-invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + DBGEN: DISABLED # Width: 1b, Description: Non-secure Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPNIDEN: DISABLED # Width: 1b, Description: Secure Non-invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPIDEN: DISABLED # Width: 1b, Description: Secure Invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + ISP_CMD_EN: DISABLED # Width: 1b, Description: ISP Boot Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + FA_CMD_EN: DISABLED # Width: 1b, Description: FA Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + ME_CMD_EN: DISABLED # Width: 1b, Description: Flash Mass Erase Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled ENABLE_FA_MODE: # Enable FA Mode. SET_FA_MODE Command should write 0xC33CA55A to this word to indicate boot ROM to enter FA mode. value: '0x00000000' # The value width: 32b CMPA_PROG_IN_PROGRESS: # CMPA Page Programming On Going. Set this field 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. value: '0x00000000' # The value width: 32b CMAC_UPD: # CMAC Update bitfields: # The register bitfields - IMG_UPD: CMAC_UPD_IMG_UPD_NOTHING_CHANGED # Width: 2b[0-3], Description: Image Updated - # - CMAC_UPD_IMG_UPD_NOTHING_CHANGED, (0): Nothing changed - # - CMAC_UPD_IMG_UPD_IMAGE0_CMAC_UPDATE, (1): Update image 0 CMAC - # - CMAC_UPD_IMG_UPD_IMAGE1_CMAC_UPDATE, (2): Update image 1 CMAC - # - CMAC_UPD_IMG_UPD_BOTH_IMG_CMAC_UPDATE, (3): Update CMAC for both images - CMPA_UPD: CMAC_UPD_CMPA_UPD_NO_ACTION # Width: 3b[0-7], Description: CMPA page updated through ROM API. Thus compute CMAC on sub-sequent boot. This field is checked only in OEM_OPEN (0x3) LC state. - # - CMAC_UPD_CMPA_UPD_NO_ACTION, (0): No action - # - CMAC_UPD_CMPA_UPD_RESERVED_0, (1): Combination is ignored. - # - CMAC_UPD_CMPA_UPD_UPDATE_IN_CMPA, (2): Update CMAC field in CMPA page. - # - CMAC_UPD_CMPA_UPD_TO_OEM_SECURE, (3): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_SECURE (0x7). - # - CMAC_UPD_CMPA_UPD_RESERVED_1, (4): Combination is ignored. - # - CMAC_UPD_CMPA_UPD_TO_OEM_CLOSED, (5): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_CLOSED (0xF). - # - CMAC_UPD_CMPA_UPD_TO_OEM_LOCKED, (6): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_LOCKED (0xCF). - # - CMAC_UPD_CMPA_UPD_RESERVED_2, (7): Combination is ignored. + IMG_UPD: NOTHING_CHANGED # Width: 2b, Description: Image Updated + # - NOTHING_CHANGED, (0): Nothing changed + # - IMAGE0_CMAC_UPDATE, (1): Update image 0 CMAC + # - IMAGE1_CMAC_UPDATE, (2): Update image 1 CMAC + # - BOTH_IMG_CMAC_UPDATE, (3): Update CMAC for both images + CMPA_UPD: NO_ACTION # Width: 3b, Description: CMPA page updated through ROM API. Thus compute CMAC on sub-sequent boot. This field is checked only in OEM_OPEN (0x3) LC state. + # - NO_ACTION, (0): No action + # - RESERVED_0, (1): Combination is ignored. + # - UPDATE_IN_CMPA, (2): Update CMAC field in CMPA page. + # - TO_OEM_SECURE, (3): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_SECURE (0x7). + # - RESERVED_1, (4): Combination is ignored. + # - TO_OEM_CLOSED, (5): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_CLOSED (0xF). + # - TO_OEM_LOCKED, (6): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_LOCKED (0xCF). + # - RESERVED_2, (7): Combination is ignored. IMG0_CMAC0: # Image 0 CMAC 0 value: '0x00000000' # The value width: 32b IMG0_CMAC1: # Image 0 CMAC 1 diff --git a/tests/pfr/data/cfpa_lpc55s3x_default_full.yaml b/tests/pfr/data/cfpa_lpc55s3x_default_full.yaml index ee28e2ca..ffb18d4e 100644 --- a/tests/pfr/data/cfpa_lpc55s3x_default_full.yaml +++ b/tests/pfr/data/cfpa_lpc55s3x_default_full.yaml @@ -1,15 +1,17 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s3x PFR CFPA configuration -description: # The PFR CFPA configuration description. +description: # The CFPA configuration description. device: lpc55s3x # The NXP device name. revision: 1a # The NXP device revision. - type: CFPA # The PFR type (CMPA, CFPA). - version: 1.7.1 # The SPSDK tool version. - author: NXP # The author of the configuration. - release: alpha # The SPSDK release. -settings: # The PFR CFPA registers configuration. + type: CFPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.1 # The SPSDK tool version. +settings: # The CFPA registers configuration. HEADER: # Header value: '0x00000000' # The value width: 32b - CFPA_PAGE_VERSION: # CFPA Page Version + CFPA_PAGE_VERSION: # CFPA Page Version. Use value of 0xFFFF_FFFF to auto increment current CFPA page version value (by ROM). value: '0x00000000' # The value width: 32b S_FW_Version: # Secure Firmware Version (Monotonic counter) value: '0x00000000' # The value width: 32b @@ -49,110 +51,104 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b ROTKH_REVOKE: # Root of Trust Key Hash Revoke bitfields: # The register bitfields - RoTK0_EN: ROTKH_REVOKE_RoTK0_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 0 Enable - # - ROTKH_REVOKE_RoTK0_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK0_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK0_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK0_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK1_EN: ROTKH_REVOKE_RoTK1_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 1 Enable - # - ROTKH_REVOKE_RoTK1_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK1_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK1_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK1_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK2_EN: ROTKH_REVOKE_RoTK2_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 2 Enable - # - ROTKH_REVOKE_RoTK2_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK2_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK2_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK2_EN_KEY_REVOKED_1, (3): Key Revoked - RoTK3_EN: ROTKH_REVOKE_RoTK3_EN_ENABLED_0 # Width: 2b[0-3], Description: Root of Trust Key 3 Enable - # - ROTKH_REVOKE_RoTK3_EN_ENABLED_0, (0): Enabled - # - ROTKH_REVOKE_RoTK3_EN_ENABLED_1, (1): Enabled - # - ROTKH_REVOKE_RoTK3_EN_KEY_REVOKED_0, (2): Key Revoked - # - ROTKH_REVOKE_RoTK3_EN_KEY_REVOKED_1, (3): Key Revoked - DICE_SKIP_CSR: ROTKH_REVOKE_DICE_SKIP_CSR_GENERATE_CSR # Width: 2b[0-3], Description: DICE Skip CSR - # - ROTKH_REVOKE_DICE_SKIP_CSR_GENERATE_CSR, (0): Generate CSR - # - ROTKH_REVOKE_DICE_SKIP_CSR_SKIP_CSR, (1): Skip CSR + RoTK0_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 0 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK1_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 1 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK2_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 2 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + RoTK3_EN: ENABLED_0 # Width: 2b, Description: Root of Trust Key 3 Enable + # - ENABLED_0, (0): Enabled + # - ENABLED_1, (1): Enabled + # - KEY_REVOKED_0, (2): Key Revoked + # - KEY_REVOKED_1, (3): Key Revoked + DICE_SKIP_CSR: GENERATE_CSR # Width: 2b, Description: DICE Skip CSR + # - GENERATE_CSR, (0): Generate CSR + # - SKIP_CSR, (1): Skip CSR VENDOR_USAGE: # Vendor Usage bitfields: # The register bitfields - DBG_VENDOR_USAGE: 0 # Width: 16b[0-65535], Description: Debug Vendor Usage - INVERSE_VALUE: 0 # Width: 16b[0-65535], Description: Inverse Value + DBG_VENDOR_USAGE: '0x0000' # Width: 16b, Description: Debug Vendor Usage + INVERSE_VALUE: '0x0000' # Width: 16b, Description: Inverse Value DCFG_CC_SOCU_NS_PIN: # Device Configuration Credential Constraints for SoC specific Use Pinned bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP # Width: 1b[0-1], Description: Non-secure Non-invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_FIXED_STATE, (1): Fixed state - DBGEN: DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP # Width: 1b[0-1], Description: Non-secure Debug Enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_FIXED_STATE, (1): Fixed state - SPNIDEN: DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP # Width: 1b[0-1], Description: Secure Non-invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_FIXED_STATE, (1): Fixed state - SPIDEN: DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP # Width: 1b[0-1], Description: Secure Invasive Debug Enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_FIXED_STATE, (1): Fixed state - DSP_DBGEN: DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_USE_DAP # Width: 1b[0-1], Description: DSP Debug Enable - # - DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_DSP_DBGEN_FIXED_STATE, (1): Fixed state - ISP_CMD_EN: DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP # Width: 1b[0-1], Description: ISP Boot Command Enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_FIXED_STATE, (1): Fixed state - FA_CMD_EN: DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_USE_DAP # Width: 1b[0-1], Description: FA Command Enable - # - DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_FA_CMD_EN_FIXED_STATE, (1): Fixed state - ME_CMD_EN: DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_USE_DAP # Width: 1b[0-1], Description: Flash Mass Erase Command Enable - # - DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_ME_CMD_EN_FIXED_STATE, (1): Fixed state - UUID_CHECK: DCFG_CC_SOCU_NS_PIN_UUID_CHECK_DISABLED # Width: 1b[0-1], Description: UUID Check - # - DCFG_CC_SOCU_NS_PIN_UUID_CHECK_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_PIN_UUID_CHECK_ENABLED, (1): Enabled - INVERSE_VALUE: 0 # Width: 16b[0-65535], Description: Inverse Value of Bits [15:0] + NIDEN: USE_DAP # Width: 1b, Description: Non-secure Non-invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DBGEN: USE_DAP # Width: 1b, Description: Non-secure Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPNIDEN: USE_DAP # Width: 1b, Description: Secure Non-invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPIDEN: USE_DAP # Width: 1b, Description: Secure Invasive Debug Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ISP_CMD_EN: USE_DAP # Width: 1b, Description: ISP Boot Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + FA_CMD_EN: USE_DAP # Width: 1b, Description: FA Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ME_CMD_EN: USE_DAP # Width: 1b, Description: Flash Mass Erase Command Enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + UUID_CHECK: DISABLED # Width: 1b, Description: Enforce UUID match during debug authentication. + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + INVERSE_VALUE: '0x0000' # Width: 16b, Description: Inverse Value of Bits [15:0] DCFG_CC_SOCU_NS_DFLT: # Device Configuration Credential Constraints for SoC specific Use Debug Filter bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED # Width: 1b[0-1], Description: Non-secure Non-invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_ENABLED, (1): Enabled - DBGEN: DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED # Width: 1b[0-1], Description: Non-secure Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_ENABLED, (1): Enabled - SPNIDEN: DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED # Width: 1b[0-1], Description: Secure Non-invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_ENABLED, (1): Enabled - SPIDEN: DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED # Width: 1b[0-1], Description: Secure Invasive Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_ENABLED, (1): Enabled - DSP_DBGEN: DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_DISABLED # Width: 1b[0-1], Description: DSP Debug Fixed State - # - DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_DSP_DBGEN_ENABLED, (1): Enabled - ISP_CMD_EN: DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED # Width: 1b[0-1], Description: ISP Boot Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_ENABLED, (1): Enabled - FA_CMD_EN: DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_DISABLED # Width: 1b[0-1], Description: FA Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_FA_CMD_EN_ENABLED, (1): Enabled - ME_CMD_EN: DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_DISABLED # Width: 1b[0-1], Description: Flash Mass Erase Command Fixed State - # - DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_ME_CMD_EN_ENABLED, (1): Enabled - INVERSE_VALUE: 0 # Width: 16b[0-65535], Description: Inverse Value + NIDEN: DISABLED # Width: 1b, Description: Non-secure Non-invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + DBGEN: DISABLED # Width: 1b, Description: Non-secure Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPNIDEN: DISABLED # Width: 1b, Description: Secure Non-invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPIDEN: DISABLED # Width: 1b, Description: Secure Invasive Debug Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + ISP_CMD_EN: DISABLED # Width: 1b, Description: ISP Boot Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + FA_CMD_EN: DISABLED # Width: 1b, Description: FA Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + ME_CMD_EN: DISABLED # Width: 1b, Description: Flash Mass Erase Command Fixed State + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + INVERSE_VALUE: '0x0000' # Width: 16b, Description: Inverse Value ENABLE_FA_MODE: # Enable FA Mode. SET_FA_MODE Command should write 0xC33CA55A to this word to indicate boot ROM to enter FA mode. value: '0x00000000' # The value width: 32b CMPA_PROG_IN_PROGRESS: # CMPA Page Programming On Going. Set this field 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. value: '0x00000000' # The value width: 32b CMAC_UPD: # CMAC Update bitfields: # The register bitfields - IMG_UPD: CMAC_UPD_IMG_UPD_NOTHING_CHANGED # Width: 2b[0-3], Description: Image Updated - # - CMAC_UPD_IMG_UPD_NOTHING_CHANGED, (0): Nothing changed - # - CMAC_UPD_IMG_UPD_IMAGE0_CMAC_UPDATE, (1): Update image 0 CMAC - # - CMAC_UPD_IMG_UPD_IMAGE1_CMAC_UPDATE, (2): Update image 1 CMAC - # - CMAC_UPD_IMG_UPD_BOTH_IMG_CMAC_UPDATE, (3): Update CMAC for both images - CMPA_UPD: CMAC_UPD_CMPA_UPD_NO_ACTION # Width: 3b[0-7], Description: CMPA page updated through ROM API. Thus compute CMAC on sub-sequent boot. This field is checked only in OEM_OPEN (0x3) LC state. - # - CMAC_UPD_CMPA_UPD_NO_ACTION, (0): No action - # - CMAC_UPD_CMPA_UPD_RESERVED_0, (1): Combination is ignored. - # - CMAC_UPD_CMPA_UPD_UPDATE_IN_CMPA, (2): Update CMAC field in CMPA page. - # - CMAC_UPD_CMPA_UPD_TO_OEM_SECURE, (3): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_SECURE (0x7). - # - CMAC_UPD_CMPA_UPD_RESERVED_1, (4): Combination is ignored. - # - CMAC_UPD_CMPA_UPD_TO_OEM_CLOSED, (5): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_CLOSED (0xF). - # - CMAC_UPD_CMPA_UPD_TO_OEM_LOCKED, (6): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_LOCKED (0xCF). - # - CMAC_UPD_CMPA_UPD_RESERVED_2, (7): Combination is ignored. + IMG_UPD: NOTHING_CHANGED # Width: 2b, Description: Image Updated + # - NOTHING_CHANGED, (0): Nothing changed + # - IMAGE0_CMAC_UPDATE, (1): Update image 0 CMAC + # - IMAGE1_CMAC_UPDATE, (2): Update image 1 CMAC + # - BOTH_IMG_CMAC_UPDATE, (3): Update CMAC for both images + CMPA_UPD: NO_ACTION # Width: 3b, Description: CMPA page updated through ROM API. Thus compute CMAC on sub-sequent boot. This field is checked only in OEM_OPEN (0x3) LC state. + # - NO_ACTION, (0): No action + # - RESERVED_0, (1): Combination is ignored. + # - UPDATE_IN_CMPA, (2): Update CMAC field in CMPA page. + # - TO_OEM_SECURE, (3): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_SECURE (0x7). + # - RESERVED_1, (4): Combination is ignored. + # - TO_OEM_CLOSED, (5): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_CLOSED (0xF). + # - TO_OEM_LOCKED, (6): Update CMAC field in CMPA page and OTP. Advance OTP_LC_STATE to OEM_LOCKED (0xCF). + # - RESERVED_2, (7): Combination is ignored. IMG0_CMAC0: # Image 0 CMAC 0 value: '0x00000000' # The value width: 32b IMG0_CMAC1: # Image 0 CMAC 1 diff --git a/tests/pfr/data/cfpa_no_change.yml b/tests/pfr/data/cfpa_no_change.yml index 507dbcb9..0011db9d 100644 --- a/tests/pfr/data/cfpa_no_change.yml +++ b/tests/pfr/data/cfpa_no_change.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s3x PFR CFPA configuration description: # The PFR CFPA configuration description. device: lpc55s3x # The NXP device name. diff --git a/tests/pfr/data/cfpa_pfrc.yml b/tests/pfr/data/cfpa_pfrc.yml index d94794ca..171687a3 100644 --- a/tests/pfr/data/cfpa_pfrc.yml +++ b/tests/pfr/data/cfpa_pfrc.yml @@ -1,9 +1,13 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s6x PFR CFPA configuration description: # The PFR CFPA configuration description. device: lpc55s6x # The NXP device name. revision: 1b # The NXP device revision. - type: CFPA # The PFR type (CMPA, CFPA). - version: 1.5.0 # The SPSDK tool version. + type: CFPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.1 # The SPSDK tool version. author: NXP # The author of the configuration. release: alpha # The SPSDK release. settings: # The PFR CFPA registers configuration. @@ -19,88 +23,90 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b ROTKH_REVOKE: # Root of Trust Key Hash Revoke bitfields: # The register bitfields - RoTK0_EN: ROTKH_REVOKE_RoTK0_EN_ENABLED # Width: 2b[0-3], Description: RoT Key 0 enable. - # - ROTKH_REVOKE_RoTK0_EN_INVALID, (0): Invalid - # - ROTKH_REVOKE_RoTK0_EN_ENABLED, (1): Enabled - # - ROTKH_REVOKE_RoTK0_EN_REVOKED_0, (2): Key revoked - # - ROTKH_REVOKE_RoTK0_EN_REVOKED_1, (3): Key revoked - RoTK1_EN: ROTKH_REVOKE_RoTK1_EN_ENABLED # Width: 2b[0-3], Description: RoT Key 1 enable. - # - ROTKH_REVOKE_RoTK1_EN_INVALID, (0): Invalid - # - ROTKH_REVOKE_RoTK1_EN_ENABLED, (1): Enabled - # - ROTKH_REVOKE_RoTK1_EN_REVOKED_0, (2): Key revoked - # - ROTKH_REVOKE_RoTK1_EN_REVOKED_1, (3): Key revoked - RoTK2_EN: ROTKH_REVOKE_RoTK2_EN_ENABLED # Width: 2b[0-3], Description: RoT Key 2 enable. - # - ROTKH_REVOKE_RoTK2_EN_INVALID, (0): Invalid - # - ROTKH_REVOKE_RoTK2_EN_ENABLED, (1): Enabled - # - ROTKH_REVOKE_RoTK2_EN_REVOKED_0, (2): Key revoked - # - ROTKH_REVOKE_RoTK2_EN_REVOKED_1, (3): Key revoked - RoTK3_EN: ROTKH_REVOKE_RoTK3_EN_ENABLED # Width: 2b[0-3], Description: RoT Key 3 enable. - # - ROTKH_REVOKE_RoTK3_EN_INVALID, (0): Invalid - # - ROTKH_REVOKE_RoTK3_EN_ENABLED, (1): Enabled - # - ROTKH_REVOKE_RoTK3_EN_REVOKED_0, (2): Key revoked - # - ROTKH_REVOKE_RoTK3_EN_REVOKED_1, (3): Key revoked + RoTK0_EN: ENABLED # Width: 2b, Description: RoT Key 0 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK1_EN: ENABLED # Width: 2b, Description: RoT Key 1 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK2_EN: ENABLED # Width: 2b, Description: RoT Key 2 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked + RoTK3_EN: ENABLED # Width: 2b, Description: RoT Key 3 enable. + # - INVALID, (0): Invalid + # - ENABLED, (1): Enabled + # - REVOKED_0, (2): Key revoked + # - REVOKED_1, (3): Key revoked VENDOR_USAGE: # Vendor Usage bitfields: # The register bitfields - DBG_VENDOR_USAGE: 0 # Width: 16b[0-65535], Description: Debug Vendor Usage + DBG_VENDOR_USAGE: 0 # Width: 16b, Description: Debug Vendor Usage DCFG_CC_SOCU_NS_PIN: # Device Configuration Credential Constraints for SoC specific Use Pinned bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP # Width: 1b[0-1], Description: Non Secure non-invasive debug enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_NIDEN_FIXED_STATE, (1): Fixed state - DBGEN: DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP # Width: 1b[0-1], Description: Non Secure debug enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_DBGEN_FIXED_STATE, (1): Fixed state - SPNIDEN: DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP # Width: 1b[0-1], Description: Secure non-invasive debug enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPNIDEN_FIXED_STATE, (1): Fixed state - SPIDEN: DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP # Width: 1b[0-1], Description: Secure invasive debug enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_SPIDEN_FIXED_STATE, (1): Fixed state - TAPEN: DCFG_CC_SOCU_NS_PIN_TAPEN_USE_DAP # Width: 1b[0-1], Description: JTAG TAP enable - # - DCFG_CC_SOCU_NS_PIN_TAPEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_TAPEN_FIXED_STATE, (1): Fixed state - MCM33_DBGEN: DCFG_CC_SOCU_NS_PIN_MCM33_DBGEN_USE_DAP # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug enable - # - DCFG_CC_SOCU_NS_PIN_MCM33_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_MCM33_DBGEN_FIXED_STATE, (1): Fixed state - ISP_CMD_EN: DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP # Width: 1b[0-1], Description: ISP Boot Command enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_ISP_CMD_EN_FIXED_STATE, (1): Fixed state - FA_ME_CMD_EN: DCFG_CC_SOCU_NS_PIN_FA_ME_CMD_EN_USE_DAP # Width: 1b[0-1], Description: Fault Analysis/Mass Erase Command enable - # - DCFG_CC_SOCU_NS_PIN_FA_ME_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_FA_ME_CMD_EN_FIXED_STATE, (1): Fixed state - MCM33_NIDEN: DCFG_CC_SOCU_NS_PIN_MCM33_NIDEN_USE_DAP # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug enable - # - DCFG_CC_SOCU_NS_PIN_MCM33_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_NS_PIN_MCM33_NIDEN_FIXED_STATE, (1): Fixed state - UUID_CHECK: 0 # Width: 1b[0-1], Description: Enforce UUID match during Debug authentication. + NIDEN: USE_DAP # Width: 1b, Description: Non Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DBGEN: USE_DAP # Width: 1b, Description: Non Secure debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPNIDEN: USE_DAP # Width: 1b, Description: Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPIDEN: USE_DAP # Width: 1b, Description: Secure invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + TAPEN: USE_DAP # Width: 1b, Description: JTAG TAP enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_DBGEN: USE_DAP # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ISP_CMD_EN: USE_DAP # Width: 1b, Description: ISP Boot Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + FA_ME_CMD_EN: USE_DAP # Width: 1b, Description: Fault Analysis/Mass Erase Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_NIDEN: USE_DAP # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + UUID_CHECK: DISABLED # Width: 1b, Description: Enforce UUID match during Debug authentication. + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled DCFG_CC_SOCU_NS_DFLT: # Device Configuration Credential Constraints for SoC specific Use Debug Filter bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_NIDEN_ENABLED, (1): Enabled - DBGEN: DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED # Width: 1b[0-1], Description: Non Secure debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_DBGEN_ENABLED, (1): Enabled - SPNIDEN: DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED # Width: 1b[0-1], Description: Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPNIDEN_ENABLED, (1): Enabled - SPIDEN: DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED # Width: 1b[0-1], Description: Secure invasive debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_SPIDEN_ENABLED, (1): Enabled - TAPEN: DCFG_CC_SOCU_NS_DFLT_TAPEN_DISABLED # Width: 1b[0-1], Description: JTAG TAP fixed state - # - DCFG_CC_SOCU_NS_DFLT_TAPEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_TAPEN_ENABLED, (1): Enabled - MCM33_DBGEN: DCFG_CC_SOCU_NS_DFLT_MCM33_DBGEN_DISABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_MCM33_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_MCM33_DBGEN_ENABLED, (1): Enabled - ISP_CMD_EN: DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED # Width: 1b[0-1], Description: ISP Boot Command fixed state - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_ISP_CMD_EN_ENABLED, (1): Enabled - FA_ME_CMD_EN: DCFG_CC_SOCU_NS_DFLT_FA_ME_CMD_EN_DISABLED # Width: 1b[0-1], Description: Fault Analysis/Mass Erase Command fixed state - # - DCFG_CC_SOCU_NS_DFLT_FA_ME_CMD_EN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_FA_ME_CMD_EN_ENABLED, (1): Enabled - MCM33_NIDEN: DCFG_CC_SOCU_NS_DFLT_MCM33_NIDEN_DISABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug fixed state - # - DCFG_CC_SOCU_NS_DFLT_MCM33_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_NS_DFLT_MCM33_NIDEN_ENABLED, (1): Enabled + NIDEN: DISABLED # Width: 1b, Description: Non Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + DBGEN: DISABLED # Width: 1b, Description: Non Secure debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPNIDEN: DISABLED # Width: 1b, Description: Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPIDEN: DISABLED # Width: 1b, Description: Secure invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + TAPEN: DISABLED # Width: 1b, Description: JTAG TAP fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_DBGEN: DISABLED # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + ISP_CMD_EN: DISABLED # Width: 1b, Description: ISP Boot Command fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + FA_ME_CMD_EN: DISABLED # Width: 1b, Description: Fault Analysis/Mass Erase Command fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_NIDEN: DISABLED # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled ENABLE_FA_MODE: # Enable FA mode. SET_FA_MODE Command should write 0xC33CA55A to this word to indicate boot ROM to enter FA mode. value: '0x00000000' # The value width: 32b CMPA_PROG_IN_PROGRESS: # 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. @@ -109,9 +115,9 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b PRINCE_REGION0_IV_HEADER1: # Header. bitfields: # The register bitfields - TYPE: 0 # Width: 2b[0-3], Description: Type. - INDEX: 0 # Width: 4b[0-15], Description: Index. - SIZE: 0 # Width: 6b[0-63], Description: Size. + TYPE: '0x0' # Width: 2b, Description: Type. + INDEX: '0x0' # Width: 4b, Description: Index. + SIZE: '0x0' # Width: 6b, Description: Size. PRINCE_REGION0_IV_BODY0: # Field. value: '0x00000000' # The value width: 32b PRINCE_REGION0_IV_BODY1: # Field. @@ -140,9 +146,9 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b PRINCE_REGION1_IV_HEADER1: # Header. bitfields: # The register bitfields - TYPE: 0 # Width: 2b[0-3], Description: Type. - INDEX: 0 # Width: 4b[0-15], Description: Index. - SIZE: 0 # Width: 6b[0-63], Description: Size. + TYPE: '0x0' # Width: 2b, Description: Type. + INDEX: '0x0' # Width: 4b, Description: Index. + SIZE: '0x0' # Width: 6b, Description: Size. PRINCE_REGION1_IV_BODY0: # Field. value: '0x00000000' # The value width: 32b PRINCE_REGION1_IV_BODY1: # Field. @@ -171,9 +177,9 @@ settings: # The PFR CFPA registers configuration. value: '0x00000000' # The value width: 32b PRINCE_REGION2_IV_HEADER1: # Header. bitfields: # The register bitfields - TYPE: 0 # Width: 2b[0-3], Description: Type. - INDEX: 0 # Width: 4b[0-15], Description: Index. - SIZE: 0 # Width: 6b[0-63], Description: Size. + TYPE: '0x0' # Width: 2b, Description: Type. + INDEX: '0x0' # Width: 4b, Description: Index. + SIZE: '0x0' # Width: 6b, Description: Size. PRINCE_REGION2_IV_BODY0: # Field. value: '0x00000000' # The value width: 32b PRINCE_REGION2_IV_BODY1: # Field. diff --git a/tests/pfr/data/cfpa_pfrc_lpc55s3x.yml b/tests/pfr/data/cfpa_pfrc_lpc55s3x.yml index cdd8e646..89510898 100644 --- a/tests/pfr/data/cfpa_pfrc_lpc55s3x.yml +++ b/tests/pfr/data/cfpa_pfrc_lpc55s3x.yml @@ -1,9 +1,13 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s3x PFR CFPA configuration -description: # The PFR CFPA configuration description. +description: # The CFPA configuration description. device: lpc55s3x # The NXP device name. revision: 0a # The NXP device revision. - type: CFPA # The PFR type (CMPA, CFPA). - version: 1.6.0 # The SPSDK tool version. + type: CFPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.1 # The SPSDK tool version. author: NXP # The author of the configuration. release: alpha # The SPSDK release. settings: # The PFR CFPA registers configuration. diff --git a/tests/pfr/data/cfpa_test.yml b/tests/pfr/data/cfpa_test.yml index e518c8ca..5553403c 100644 --- a/tests/pfr/data/cfpa_test.yml +++ b/tests/pfr/data/cfpa_test.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP LPC55S6X PFR spsdk.pfr.pfr configuration description: # The PFR spsdk.pfr.pfr configuration description. device: lpc55s6x # The NXP device name. diff --git a/tests/pfr/data/cmpa_96mhz.json b/tests/pfr/data/cmpa_96mhz.json index e3a03b78..961ef8ff 100644 --- a/tests/pfr/data/cmpa_96mhz.json +++ b/tests/pfr/data/cmpa_96mhz.json @@ -12,18 +12,18 @@ "SPNIDEN": "0b1", "SPIDEN": "0b1", "TAPEN": "0b1", - "MCM33_DBGEN": "0b1", + "CPU1_DBGEN": "0b1", "ISP_CMD_EN": "0b1", "FA_ME_CMD_EN": "0b1", - "MCM33_NIDEN": "0b1" + "CPU1_NIDEN": "0b1" }, "DCFG_CC_SOCU_DFLT": { "NIDEN": "0b1", "DBGEN": "0b1", "SPNIDEN": "0b1", "SPIDEN": "0b1", - "MCM33_DBGEN": "0b1", - "MCM33_NIDEN": "0b1" + "CPU1_DBGEN": "0b1", + "CPU1_NIDEN": "0b1" }, "SECURE_BOOT_CFG": { "SKIP_DICE": "0b01", diff --git a/tests/pfr/data/cmpa_96mhz.yml b/tests/pfr/data/cmpa_96mhz.yml index 464f8354..c622a401 100644 --- a/tests/pfr/data/cmpa_96mhz.yml +++ b/tests/pfr/data/cmpa_96mhz.yml @@ -1,80 +1,85 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s6x PFR CMPA configuration description: # The PFR CMPA configuration description. device: lpc55s6x # The NXP device name. revision: 1b # The NXP device revision. - type: CMPA # The PFR type (CMPA, CFPA). - version: 1.5.0 # The SPSDK tool version. + type: CMPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.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: # Boot Configuration bitfields: # The register bitfields - BOOT_SPEED: BOOT_CFG_BOOT_SPEED_FRO_96MHZ # Width: 2b[0-3], Description: Core clock: - # - BOOT_CFG_BOOT_SPEED_SYSTEM_SPEED_CODE, (0): Defined by NMPA.SYSTEM_SPEED_CODE - # - BOOT_CFG_BOOT_SPEED_FRO_96MHZ, (1): 96MHz FRO - # - BOOT_CFG_BOOT_SPEED_FRO_48MHZ, (2): 48MHz FRO - + BOOT_SPEED: FRO_96MHZ # Width: 2b, Description: Core clock: + # - SYSTEM_SPEED_CODE, (0): Defined by NMPA.SYSTEM_SPEED_CODE + # - FRO_96MHZ, (1): 96MHz FRO + # - FRO_48MHZ, (2): 48MHz FRO DCFG_CC_SOCU_PIN: # Device Configuration Credential Constraints for SoC specific Use Pinned bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_PIN_NIDEN_FIXED_STATE # Width: 1b[0-1], Description: Non Secure non-invasive debug enable - # - DCFG_CC_SOCU_PIN_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_NIDEN_FIXED_STATE, (1): Fixed state - DBGEN: DCFG_CC_SOCU_PIN_DBGEN_FIXED_STATE # Width: 1b[0-1], Description: Non Secure debug enable - # - DCFG_CC_SOCU_PIN_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_DBGEN_FIXED_STATE, (1): Fixed state - SPNIDEN: DCFG_CC_SOCU_PIN_SPNIDEN_FIXED_STATE # Width: 1b[0-1], Description: Secure non-invasive debug enable - # - DCFG_CC_SOCU_PIN_SPNIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_SPNIDEN_FIXED_STATE, (1): Fixed state - SPIDEN: DCFG_CC_SOCU_PIN_SPIDEN_FIXED_STATE # Width: 1b[0-1], Description: Secure invasive debug enable - # - DCFG_CC_SOCU_PIN_SPIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_SPIDEN_FIXED_STATE, (1): Fixed state - TAPEN: DCFG_CC_SOCU_PIN_TAPEN_FIXED_STATE # Width: 1b[0-1], Description: JTAG TAP enable - # - DCFG_CC_SOCU_PIN_TAPEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_TAPEN_FIXED_STATE, (1): Fixed state - MCM33_DBGEN: DCFG_CC_SOCU_PIN_MCM33_DBGEN_FIXED_STATE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug enable - # - DCFG_CC_SOCU_PIN_MCM33_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_MCM33_DBGEN_FIXED_STATE, (1): Fixed state - ISP_CMD_EN: DCFG_CC_SOCU_PIN_ISP_CMD_EN_FIXED_STATE # Width: 1b[0-1], Description: ISP Boot Command enable - # - DCFG_CC_SOCU_PIN_ISP_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_ISP_CMD_EN_FIXED_STATE, (1): Fixed state - FA_ME_CMD_EN: DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_FIXED_STATE # Width: 1b[0-1], Description: Fault Analysis/Mass Erase Command enable - # - DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_FIXED_STATE, (1): Fixed state - MCM33_NIDEN: DCFG_CC_SOCU_PIN_MCM33_NIDEN_FIXED_STATE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug enable - # - DCFG_CC_SOCU_PIN_MCM33_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_MCM33_NIDEN_FIXED_STATE, (1): Fixed state - UUID_CHECK: 0 # Width: 1b[0-1], Description: Enforce UUID match during Debug authentication. + NIDEN: FIXED_STATE # Width: 1b, Description: Non Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DBGEN: FIXED_STATE # Width: 1b, Description: Non Secure debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPNIDEN: FIXED_STATE # Width: 1b, Description: Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPIDEN: FIXED_STATE # Width: 1b, Description: Secure invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + TAPEN: FIXED_STATE # Width: 1b, Description: JTAG TAP enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_DBGEN: FIXED_STATE # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ISP_CMD_EN: FIXED_STATE # Width: 1b, Description: ISP Boot Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + FA_ME_CMD_EN: FIXED_STATE # Width: 1b, Description: Fault Analysis/Mass Erase Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_NIDEN: FIXED_STATE # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + UUID_CHECK: DISABLED # Width: 1b, Description: Enforce UUID match during Debug authentication. + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled DCFG_CC_SOCU_DFLT: # Device Configuration Credential Constraints for SoC specific Use Debug Filter bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_DFLT_NIDEN_ENABLED # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_NIDEN_ENABLED, (1): Enabled - DBGEN: DCFG_CC_SOCU_DFLT_DBGEN_ENABLED # Width: 1b[0-1], Description: Non Secure debug fixed state - # - DCFG_CC_SOCU_DFLT_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_DBGEN_ENABLED, (1): Enabled - SPNIDEN: DCFG_CC_SOCU_DFLT_SPNIDEN_ENABLED # Width: 1b[0-1], Description: Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_SPNIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_SPNIDEN_ENABLED, (1): Enabled - SPIDEN: DCFG_CC_SOCU_DFLT_SPIDEN_ENABLED # Width: 1b[0-1], Description: Secure invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_SPIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_SPIDEN_ENABLED, (1): Enabled - MCM33_DBGEN: DCFG_CC_SOCU_DFLT_MCM33_DBGEN_ENABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_MCM33_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_MCM33_DBGEN_ENABLED, (1): Enabled - MCM33_NIDEN: DCFG_CC_SOCU_DFLT_MCM33_NIDEN_ENABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_MCM33_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_MCM33_NIDEN_ENABLED, (1): Enabled + NIDEN: ENABLED # Width: 1b, Description: Non Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + DBGEN: ENABLED # Width: 1b, Description: Non Secure debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPNIDEN: ENABLED # Width: 1b, Description: Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPIDEN: ENABLED # Width: 1b, Description: Secure invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_DBGEN: ENABLED # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_NIDEN: ENABLED # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled SECURE_BOOT_CFG: # Secure boot configuration bitfields: # The register bitfields - SKIP_DICE: SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 # Width: 2b[0-3], Description: Skip DICE computation - # - SECURE_BOOT_CFG_SKIP_DICE_ENABLE, (0): Enable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0, (1): Disable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_1, (2): Disable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_2, (3): Disable DICE - SEC_BOOT_EN: SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0 # Width: 2b[0-3], Description: Secure boot enable - # - SECURE_BOOT_CFG_SEC_BOOT_EN_DISABLE, (0): Plain image (internal flash with or without CRC) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0, (1): Boot signed images. (internal flash, RSA signed) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_1, (2): Boot signed images. (internal flash, RSA signed) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_2, (3): Boot signed images. (internal flash, RSA signed) + SKIP_DICE: DISABLE_0 # Width: 2b, Description: Skip DICE computation + # - ENABLE, (0): Enable DICE + # - DISABLE_0, (1): Disable DICE + # - DISABLE_1, (2): Disable DICE + # - DISABLE_2, (3): Disable DICE + SEC_BOOT_EN: ENABLE_0 # Width: 2b, Description: Secure boot enable + # - DISABLE, (0): Plain image (internal flash with or without CRC) + # - ENABLE_0, (1): Boot signed images. (internal flash, RSA signed) + # - ENABLE_1, (2): Boot signed images. (internal flash, RSA signed) + # - ENABLE_2, (3): Boot signed images. (internal flash, RSA signed) ROTKH: # ROTKH field is compounded by 8 32-bit fields and contains Root key table hash value: '0x0000000000000000000000000000000000000000000000000000000000000000' # The value width: 256b diff --git a/tests/pfr/data/cmpa_96mhz_rotkh.yml b/tests/pfr/data/cmpa_96mhz_rotkh.yml index 5bfa3edb..c62e26c5 100644 --- a/tests/pfr/data/cmpa_96mhz_rotkh.yml +++ b/tests/pfr/data/cmpa_96mhz_rotkh.yml @@ -1,78 +1,82 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # NXP lpc55s6x PFR CMPA configuration description: # The PFR CMPA configuration description. device: lpc55s6x # The NXP device name. revision: 1b # The NXP device revision. - type: CMPA # The PFR type (CMPA, CFPA). - version: 1.3.5 # The SPSDK tool version. + type: CMPA # The PFR type (CMPA, CFPA) or IFR type. + version: 1.9.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: # BOOT_CFG. bitfields: # The register bitfields - BOOT_SPEED: BOOT_CFG_BOOT_SPEED_FRO_96MHZ # Width: 2b[0-3], Description: Core clock: - # - BOOT_CFG_BOOT_SPEED_SYSTEM_SPEED_CODE, (0): Defined by NMPA.SYSTEM_SPEED_CODE - # - BOOT_CFG_BOOT_SPEED_FRO_96MHZ, (1): 96MHz FRO - # - BOOT_CFG_BOOT_SPEED_FRO_48MHZ, (2): 48MHz FRO - DCFG_CC_SOCU_PIN: # DCFG_CC_SOCU_PIN. + BOOT_SPEED: FRO_96MHZ # Width: 2b, Description: Core clock: + # - SYSTEM_SPEED_CODE, (0): Defined by NMPA.SYSTEM_SPEED_CODE + # - FRO_96MHZ, (1): 96MHz FRO + # - FRO_48MHZ, (2): 48MHz FRO + DCFG_CC_SOCU_PIN: # Device Configuration Credential Constraints for SoC specific Use Pinned bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_PIN_NIDEN_FIXED_STATE # Width: 1b[0-1], Description: Non Secure non-invasive debug enable - # - DCFG_CC_SOCU_PIN_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_NIDEN_FIXED_STATE, (1): Fixed state - DBGEN: DCFG_CC_SOCU_PIN_DBGEN_FIXED_STATE # Width: 1b[0-1], Description: Non Secure debug enable - # - DCFG_CC_SOCU_PIN_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_DBGEN_FIXED_STATE, (1): Fixed state - SPNIDEN: DCFG_CC_SOCU_PIN_SPNIDEN_FIXED_STATE # Width: 1b[0-1], Description: Secure non-invasive debug enable - # - DCFG_CC_SOCU_PIN_SPNIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_SPNIDEN_FIXED_STATE, (1): Fixed state - SPIDEN: DCFG_CC_SOCU_PIN_SPIDEN_FIXED_STATE # Width: 1b[0-1], Description: Secure invasive debug enable - # - DCFG_CC_SOCU_PIN_SPIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_SPIDEN_FIXED_STATE, (1): Fixed state - TAPEN: DCFG_CC_SOCU_PIN_TAPEN_FIXED_STATE # Width: 1b[0-1], Description: JTAG TAP enable - # - DCFG_CC_SOCU_PIN_TAPEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_TAPEN_FIXED_STATE, (1): Fixed state - MCM33_DBGEN: DCFG_CC_SOCU_PIN_MCM33_DBGEN_FIXED_STATE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug enable - # - DCFG_CC_SOCU_PIN_MCM33_DBGEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_MCM33_DBGEN_FIXED_STATE, (1): Fixed state - ISP_CMD_EN: DCFG_CC_SOCU_PIN_ISP_CMD_EN_FIXED_STATE # Width: 1b[0-1], Description: ISP Boot Command enable - # - DCFG_CC_SOCU_PIN_ISP_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_ISP_CMD_EN_FIXED_STATE, (1): Fixed state - FA_ME_CMD_EN: DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_FIXED_STATE # Width: 1b[0-1], Description: Fault Analysis/Mass Erase Command enable - # - DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_FA_ME_CMD_EN_FIXED_STATE, (1): Fixed state - MCM33_NIDEN: DCFG_CC_SOCU_PIN_MCM33_NIDEN_FIXED_STATE # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug enable - # - DCFG_CC_SOCU_PIN_MCM33_NIDEN_USE_DAP, (0): Use DAP to enable - # - DCFG_CC_SOCU_PIN_MCM33_NIDEN_FIXED_STATE, (1): Fixed state - DCFG_CC_SOCU_DFLT: # DCFG_CC_SOCU_DFLT. + NIDEN: FIXED_STATE # Width: 1b, Description: Non Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DBGEN: FIXED_STATE # Width: 1b, Description: Non Secure debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPNIDEN: FIXED_STATE # Width: 1b, Description: Secure non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + SPIDEN: FIXED_STATE # Width: 1b, Description: Secure invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + TAPEN: FIXED_STATE # Width: 1b, Description: JTAG TAP enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_DBGEN: FIXED_STATE # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + ISP_CMD_EN: FIXED_STATE # Width: 1b, Description: ISP Boot Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + FA_ME_CMD_EN: FIXED_STATE # Width: 1b, Description: Fault Analysis/Mass Erase Command enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + CPU1_NIDEN: FIXED_STATE # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug enable + # - USE_DAP, (0): Use DAP to enable + # - FIXED_STATE, (1): Fixed state + DCFG_CC_SOCU_DFLT: # Device Configuration Credential Constraints for SoC specific Use Debug Filter bitfields: # The register bitfields - NIDEN: DCFG_CC_SOCU_DFLT_NIDEN_ENABLED # Width: 1b[0-1], Description: Non Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_NIDEN_ENABLED, (1): Enabled - DBGEN: DCFG_CC_SOCU_DFLT_DBGEN_ENABLED # Width: 1b[0-1], Description: Non Secure debug fixed state - # - DCFG_CC_SOCU_DFLT_DBGEN_DISABLE, (0): Disabled - # - DCFG_CC_SOCU_DFLT_DBGEN_ENABLE, (1): Enabled - SPNIDEN: DCFG_CC_SOCU_DFLT_SPNIDEN_ENABLED # Width: 1b[0-1], Description: Secure non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_SPNIDEN_DISABLE, (0): Disabled - # - DCFG_CC_SOCU_DFLT_SPNIDEN_ENABLE, (1): Enabled - SPIDEN: DCFG_CC_SOCU_DFLT_SPIDEN_ENABLED # Width: 1b[0-1], Description: Secure invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_SPIDEN_DISABLE, (0): Disabled - # - DCFG_CC_SOCU_DFLT_SPIDEN_ENABLE, (1): Enabled - MCM33_DBGEN: DCFG_CC_SOCU_DFLT_MCM33_DBGEN_ENABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_MCM33_DBGEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_MCM33_DBGEN_ENABLED, (1): Enabled - MCM33_NIDEN: DCFG_CC_SOCU_DFLT_MCM33_NIDEN_ENABLED # Width: 1b[0-1], Description: CPU1 (Micro cortex M33) non-invasive debug fixed state - # - DCFG_CC_SOCU_DFLT_MCM33_NIDEN_DISABLED, (0): Disabled - # - DCFG_CC_SOCU_DFLT_MCM33_NIDEN_ENABLED, (1): Enabled + NIDEN: ENABLED # Width: 1b, Description: Non Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + DBGEN: ENABLED # Width: 1b, Description: Non Secure debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPNIDEN: ENABLED # Width: 1b, Description: Secure non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + SPIDEN: ENABLED # Width: 1b, Description: Secure invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_DBGEN: ENABLED # Width: 1b, Description: CPU1 (Micro cortex M33) invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled + CPU1_NIDEN: ENABLED # Width: 1b, Description: CPU1 (Micro cortex M33) non-invasive debug fixed state + # - DISABLED, (0): Disabled + # - ENABLED, (1): Enabled SECURE_BOOT_CFG: # Secure boot configuration flags. bitfields: # The register bitfields - SKIP_DICE: SECURE_BOOT_CFG_SKIP_DICE_DISABLE_0 # Width: 2b[0-3], Description: Skip DICE computation - # - SECURE_BOOT_CFG_SKIP_DICE_ENABLE, (0): Enable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_1, (1): Disable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_2, (2): Disable DICE - # - SECURE_BOOT_CFG_SKIP_DICE_DISABLE_3, (3): Disable DICE - SEC_BOOT_EN: SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_0 # Width: 2b[0-3], Description: Secure boot enable - # - SECURE_BOOT_CFG_SEC_BOOT_EN_DISABLE, (0): Plain image (internal flash with or without CRC) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_1, (1): Boot signed images. (internal flash, RSA signed) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_2, (2): Boot signed images. (internal flash, RSA signed) - # - SECURE_BOOT_CFG_SEC_BOOT_EN_ENABLE_3, (3): Boot signed images. (internal flash, RSA signed) + SKIP_DICE: DISABLE_0 # Width: 2b, Description: Skip DICE computation + # - ENABLE, (0): Enable DICE + # - DISABLE_0, (1): Disable DICE + # - DISABLE_1, (2): Disable DICE + # - DISABLE_2, (3): Disable DICE + SEC_BOOT_EN: ENABLE_0 # Width: 2b, Description: Secure boot enable + # - DISABLE, (0): Plain image (internal flash with or without CRC) + # - ENABLE_0, (1): Boot signed images. (internal flash, RSA signed) + # - ENABLE_1, (2): Boot signed images. (internal flash, RSA signed) + # - ENABLE_2, (3): Boot signed images. (internal flash, RSA signed) ROTKH: # ROTKH field is compounded by 8 32-bit fields and contains Root key table hash value: "04355CEA4777D09D7674AD2C8E47DD38A05C434E5662FC47B83A0B2B50D22A3C" # The value width: 256b diff --git a/tests/nxpimage/data/bootable_image/rt106x/fcb.bin b/tests/pfr/data/cmpa_lpc55s3x.bin similarity index 75% rename from tests/nxpimage/data/bootable_image/rt106x/fcb.bin rename to tests/pfr/data/cmpa_lpc55s3x.bin index a64a5a93fb4aef4d5f63d79cb2582731b9ac5063..3c01c7e3777e31d470cd189e506d174bbdff07fe 100644 GIT binary patch literal 512 zcmZQzzzrC2OW~B+UNQI5qwrHvy0aN?f4tsha75IdpOfRD=GUzy1+ThRr# - - + + + - - - - - - + + + + + + - - - - - + + + + + + + + + + + + - + - + - + - + - + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - + + + - + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - + - + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -218,13 +232,13 @@ - - - - + + + + - + @@ -238,39 +252,39 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -296,8 +310,8 @@ - - + + @@ -314,8 +328,8 @@ - - + + diff --git a/tests/pfr/data/mbi_config_lpc55s3x.json b/tests/pfr/data/mbi_config_lpc55s3x.json new file mode 100644 index 00000000..4676cbb3 --- /dev/null +++ b/tests/pfr/data/mbi_config_lpc55s3x.json @@ -0,0 +1,20 @@ +{ + "family": "lpc55s3x", + "firmwareVersion": "0x1", + "inputImageFile": "C:/_DDM/git/secure_provisioning_7/tests/resources/images/lpc55xx/lpcxpresso55s36_led_blinky.bin", + "outputImageExecutionTarget": "Internal Flash (XIP)", + "outputImageAuthenticationType": "CRC", + "enableTrustZone": true, + "trustZonePresetFile": "", + "enableHwUserModeKeys": false, + "imageBuildNumber": "1", + "rootCertificate0File": "keys/ROT1_p384.pem", + "rootCertificate1File": "keys/ROT2_p384.pem", + "rootCertificate2File": "keys/ROT3_p384.pem", + "rootCertificate3File": "keys/ROT4_p384.pem", + "masterBootOutputFile": "lpcxpresso55s36_led_blinky_unsigned_with_crc.bin", + "outputImageExecutionAddress": "0x00000000", + "rootCertificateEllipticCurve": "secp384r1", + "mainRootCertId": 0, + "mainRootCertPrivateKeyFile": "keys/ROT1_p384.pem" +} diff --git a/tests/pfr/test_pfr_cli.py b/tests/pfr/test_pfr_cli.py index 2540dc49..638f5243 100644 --- a/tests/pfr/test_pfr_cli.py +++ b/tests/pfr/test_pfr_cli.py @@ -85,6 +85,18 @@ def test_generate_cmpa_with_elf2sb(data_dir, tmpdir): assert filecmp.cmp(org_file, new_file) +def test_generate_cmpa_with_elf2sb_lpc55s3x(data_dir, tmpdir): + """Test PFR CLI - Generation CMPA binary with elf2sb.""" + new = f"{tmpdir}/new.bin" + org = "cmpa_lpc55s3x.bin" + cmd = f"generate-binary --user-config cmpa_lpc55s3x.json -e mbi_config_lpc55s3x.json -o {new}" + + with use_working_directory(data_dir): + result = CliRunner().invoke(cli.main, cmd.split()) + assert result.exit_code == 0, result.output + assert filecmp.cmp(org, new) + + def test_parse(data_dir, tmpdir): """Test PFR CLI - Parsing CMPA binary to get config.""" cmd = "parse-binary --device lpc55s6x --type cmpa " diff --git a/tests/sbfile/sb31/common.py b/tests/sbfile/sb31/common.py index 73e004bd..c2767244 100644 --- a/tests/sbfile/sb31/common.py +++ b/tests/sbfile/sb31/common.py @@ -1,6 +1,7 @@ +#!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2020 NXP +# Copyright 2020-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause """Create read file function.""" diff --git a/tests/sbfile/sb31/test_functions.py b/tests/sbfile/sb31/test_functions.py index c7ec7fb6..24663b29 100644 --- a/tests/sbfile/sb31/test_functions.py +++ b/tests/sbfile/sb31/test_functions.py @@ -13,7 +13,12 @@ from spsdk import SPSDKError from spsdk.sbfile.sb31.commands import BaseCmd, CmdErase, MainCmd from spsdk.sbfile.sb31.functions import KeyDerivator, _get_key_derivation_data, derive_block_key -from spsdk.sbfile.sb31.images import SecureBinary31, SecureBinary31Commands, SecureBinary31Header +from spsdk.sbfile.sb31.images import ( + SecureBinary31, + SecureBinary31Commands, + SecureBinary31Header, + get_signature_provider, +) from spsdk.utils.crypto import CertBlockV31 from spsdk.utils.misc import load_binary @@ -208,23 +213,28 @@ def test_secure_binary3_validate(data_dir): rot = [load_binary(os.path.join(data_dir, "ecc_secp256r1_priv_key.pem")) for x in range(4)] cert_blk = CertBlockV31(root_certs=rot, ca_flag=1) + cert_blk.calculate() sb3 = SecureBinary31( family="lpc55s3x", curve_name="secp256r1", cert_block=cert_blk, firmware_version=1, - signing_key=rot[0], + signature_provider=get_signature_provider( + sp_cfg=None, local_file_key="ecc_secp256r1_priv_key.pem", search_paths=[data_dir] + ), is_encrypted=False, ) sb3.validate() with pytest.raises(SPSDKError): - sb3.signing_key = None + sb3.signature_provider = None sb3.validate() with pytest.raises(SPSDKError): - sb3.signing_key = "Invalid" + sb3.signature_provider = "Invalid" sb3.validate() - sb3.signing_key = rot[0] + sb3.signature_provider = get_signature_provider( + sp_cfg=None, local_file_key="ecc_secp256r1_priv_key.pem", search_paths=[data_dir] + ) sb3.validate() with pytest.raises(SPSDKError): sb3.curve_name = "Invalid" @@ -233,11 +243,6 @@ def test_secure_binary3_validate(data_dir): sb3.curve_name = "secp384r1" sb3.validate() sb3.curve_name = "secp256r1" - with pytest.raises(SPSDKError): - signig_key_bck = sb3.signing_key - sb3.signing_key = b"Invalid Key" - sb3.validate() - sb3.signing_key = signig_key_bck sb3.validate() @@ -246,13 +251,16 @@ def test_secure_binary3_info(data_dir): rot = [load_binary(os.path.join(data_dir, "ecc_secp256r1_priv_key.pem")) for x in range(4)] cert_blk = CertBlockV31(root_certs=rot, ca_flag=1) + cert_blk.calculate() sb3 = SecureBinary31( family="lpc55s3x", curve_name="secp256r1", cert_block=cert_blk, firmware_version=1, - signing_key=rot[0], + signature_provider=get_signature_provider( + sp_cfg=None, local_file_key="ecc_secp256r1_priv_key.pem", search_paths=[data_dir] + ), is_encrypted=False, ) info = sb3.info() @@ -266,10 +274,17 @@ def test_cert_block_validate(data_dir): rot = [load_binary(os.path.join(data_dir, "ecc_secp256r1_priv_key.pem")) for x in range(4)] isk_cert = load_binary(os.path.join(data_dir, "ec_secp256r1_cert0.pem")) cert_blk = CertBlockV31( - root_certs=rot, ca_flag=0, version="2.0", isk_private_key=rot[0], isk_cert=isk_cert + root_certs=rot, + ca_flag=0, + version="2.0", + signature_provider=get_signature_provider( + sp_cfg=None, local_file_key="ecc_secp256r1_priv_key.pem", search_paths=[data_dir] + ), + isk_cert=isk_cert, ) + cert_blk.calculate() cert_blk.validate() with pytest.raises(SPSDKError): - cert_blk.isk_certificate.isk_private_key = "invalid" + cert_blk.isk_certificate.signature_provider = "invalid" cert_blk.validate() diff --git a/tests/sbfile/test_backend_python.py b/tests/sbfile/test_backend_python.py index 24a8b623..aaaa5de3 100644 --- a/tests/sbfile/test_backend_python.py +++ b/tests/sbfile/test_backend_python.py @@ -209,7 +209,7 @@ def test_ecc_verify(data_dir): b"\xc3\x0f~\x8b\xd7<\xb02,DIr\x15\x0br\x98\x8c\xa7\x93\x0f\x19\x85" b"V\x13\xfc\x94\x07Y\xab\xcax\xc5\x15\x07\x8d\xaeQ-mE1\xc9" ) - is_valid = internal_backend.ecc_verify(key=public_key, signature=signature, data=data) + is_valid = internal_backend.ecc_verify(public_key=public_key, signature=signature, data=data) assert is_valid diff --git a/tests/shadowregs/data/sh_regs_corrupted.yml b/tests/shadowregs/data/sh_regs_corrupted.yml index 1b75101b..25c34d2b 100644 --- a/tests/shadowregs/data/sh_regs_corrupted.yml +++ b/tests/shadowregs/data/sh_regs_corrupted.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + registers: REG1: # Reg Description:Register 1, used for testing antipole register and some computed fields. diff --git a/tests/shadowregs/data/sh_test_dev_x0.xml b/tests/shadowregs/data/sh_test_dev_x0.xml index 35d32b01..5e155f68 100644 --- a/tests/shadowregs/data/sh_test_dev_x0.xml +++ b/tests/shadowregs/data/sh_test_dev_x0.xml @@ -1,4 +1,9 @@ + diff --git a/tests/shadowregs/data/test_database.yaml b/tests/shadowregs/data/test_database.yaml index f884a68b..f816adab 100644 --- a/tests/shadowregs/data/test_database.yaml +++ b/tests/shadowregs/data/test_database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: sh_test_dev: revisions: diff --git a/tests/shadowregs/data/test_database_invalid_computed.yaml b/tests/shadowregs/data/test_database_invalid_computed.yaml index 412441c6..dc1342cc 100644 --- a/tests/shadowregs/data/test_database_invalid_computed.yaml +++ b/tests/shadowregs/data/test_database_invalid_computed.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: sh_test_dev: revisions: diff --git a/tests/shadowregs/test_shadowregs.py b/tests/shadowregs/test_shadowregs.py index 99489b7e..11ae40aa 100644 --- a/tests/shadowregs/test_shadowregs.py +++ b/tests/shadowregs/test_shadowregs.py @@ -9,7 +9,6 @@ import pytest -import spsdk.debuggers.debug_probe as DP import spsdk.shadowregs.shadowregs as SR import spsdk.utils.registers as REGS from spsdk.exceptions import SPSDKError @@ -157,14 +156,14 @@ def test_shadowreg_yml(data_dir, tmpdir): assert shadowregs.get_register("REG_BIG") == test_val assert shadowregs.get_register("REG_BIG_REV") == test_val - shadowregs.create_yml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) - shadowregs.create_yml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=True) + shadowregs.create_yaml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) + shadowregs.create_yaml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=True) probe.clear() shadowregs_load_raw = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) - shadowregs_load_raw.load_yml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=True) - shadowregs_load_raw.sets_all_registers() + shadowregs_load_raw.load_yaml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=True) + shadowregs_load_raw.sets_all_registers(verify=True) assert shadowregs_load_raw.get_register("REG1") == 0x12345678.to_bytes(4, "big") assert shadowregs_load_raw.get_register("REG2") == 0x00004321.to_bytes(4, "big") @@ -175,8 +174,8 @@ def test_shadowreg_yml(data_dir, tmpdir): probe.clear() shadowregs_load = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) - shadowregs_load.load_yml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) - shadowregs_load.sets_all_registers() + shadowregs_load.load_yaml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) + shadowregs_load.sets_all_registers(verify=True) assert shadowregs_load.get_register("REG1") == b"\x92\x34\x56\x56" assert shadowregs_load.get_register("REG2") == b"\x00\x00\x03!" @@ -187,8 +186,18 @@ def test_shadowreg_yml(data_dir, tmpdir): probe.clear() shadowregs_load2 = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) - shadowregs_load2.load_yml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=False) - shadowregs_load2.sets_all_registers() + shadowregs_load2.load_yaml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=False) + shadowregs_load2.sets_all_registers(verify=True) + + assert shadowregs_load2.get_register("REG1") == b"\x92\x34\x56\x56" + assert shadowregs_load2.get_register("REG2") == b"\x00\x00\x03!" + assert shadowregs_load2.get_register("REG_INVERTED_AP") == b"m\xcb\xa9\xa9" + assert shadowregs_load2.get_register("REG_BIG") == test_val + assert shadowregs_load2.get_register("REG_BIG_REV") == test_val + + shadowregs_load2 = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) + shadowregs_load2.load_yaml_config(os.path.join(tmpdir, "sh_regs_raw.yml"), raw=False) + shadowregs_load2.sets_all_registers(verify=False) assert shadowregs_load2.get_register("REG1") == b"\x92\x34\x56\x56" assert shadowregs_load2.get_register("REG2") == b"\x00\x00\x03!" @@ -208,7 +217,7 @@ def test_shadowreg_yml_corrupted(data_dir): shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) with pytest.raises((SPSDKRegsErrorBitfieldNotFound, SPSDKRegsErrorRegisterNotFound)): - shadowregs.load_yml_config(os.path.join(data_dir, "sh_regs_corrupted.yml"), raw=True) + shadowregs.load_yaml_config(os.path.join(data_dir, "sh_regs_corrupted.yml"), raw=True) def test_shadowreg_yml_invalid_computed(tmpdir, data_dir): @@ -234,12 +243,12 @@ def test_shadowreg_yml_invalid_computed(tmpdir, data_dir): assert shadowregs.get_register("REG_BIG") == test_val assert shadowregs.get_register("REG_BIG_REV") == test_val - shadowregs.create_yml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) + shadowregs.create_yaml_config(os.path.join(tmpdir, "sh_regs.yml"), raw=False) shadowregs1 = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) with pytest.raises(SPSDKError): - shadowregs1.load_yml_config(os.path.join(tmpdir, "sh_regs.yml")) + shadowregs1.load_yaml_config(os.path.join(tmpdir, "sh_regs.yml")) def test_shadowreg_yml_none_existing(data_dir): @@ -253,7 +262,7 @@ def test_shadowreg_yml_none_existing(data_dir): shadowregs = SR.ShadowRegisters(probe, config, TEST_DEV_NAME) with pytest.raises(SPSDKError): - shadowregs.load_yml_config(os.path.join(data_dir, "sh_regs_none.yml"), raw=True) + shadowregs.load_yaml_config(os.path.join(data_dir, "sh_regs_none.yml"), raw=True) def test_shadow_register_crc8(): diff --git a/tests/test_self.py b/tests/test_self.py index 28511566..ba85bbab 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2019-2021 NXP +# Copyright 2019-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/tp/audit_log/test_audit_log.py b/tests/tp/audit_log/test_audit_log.py index 5e5d2c48..61ae08fd 100644 --- a/tests/tp/audit_log/test_audit_log.py +++ b/tests/tp/audit_log/test_audit_log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -25,7 +25,6 @@ def test_validate(data_dir): def test_create(data_dir, tmpdir): - log_file = f"{tmpdir}/audit_log.db" with open(f"{data_dir}/x_wrapped_data.bin", "rb") as f: container_data = f.read() diff --git a/tests/tp/audit_log/test_audit_log_cli.py b/tests/tp/audit_log/test_audit_log_cli.py index 2c1a7950..1d15e0f6 100644 --- a/tests/tp/audit_log/test_audit_log_cli.py +++ b/tests/tp/audit_log/test_audit_log_cli.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause @@ -14,7 +14,6 @@ def test_cli_run(data_dir): - cmd = ( f"verify --audit-log {data_dir}/tp_audit_log.db " f"--audit-log-key {data_dir}/oem_log_puk.pub" @@ -38,7 +37,6 @@ def test_cli_run(data_dir): ], ) def test_tphost_extract(data_dir, tmpdir, skip_nxp, skip_oem, cert_index, expected_count): - cmd = ( f"verify --audit-log {data_dir}/tp_audit_log.db " f"--audit-log-key {data_dir}/oem_log_puk.pub --destination {tmpdir}" diff --git a/tests/tp/data/big_oem_cert_config.yaml b/tests/tp/data/big_oem_cert_config.yaml index 8bb89410..e3e9d505 100644 --- a/tests/tp/data/big_oem_cert_config.yaml +++ b/tests/tp/data/big_oem_cert_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # The template configuration file for TPCONFIG application version: 3 diff --git a/tests/tp/data/small_oem_cert_config.yaml b/tests/tp/data/small_oem_cert_config.yaml index 39a002a9..fda96295 100644 --- a/tests/tp/data/small_oem_cert_config.yaml +++ b/tests/tp/data/small_oem_cert_config.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # The template configuration file for TPCONFIG application version: 3 diff --git a/tests/tp/data_container/conftest.py b/tests/tp/data_container/conftest.py index 96181e9e..48a0f10a 100644 --- a/tests/tp/data_container/conftest.py +++ b/tests/tp/data_container/conftest.py @@ -1,13 +1,12 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause import pytest from spsdk.tp.data_container import ( - AuthenticationType, Container, DataDestinationEntry, DataEntry, diff --git a/tests/utils/crypto/data/certs_and_keys/k0_cert0_2048.pem b/tests/utils/crypto/data/certs_and_keys/k0_cert0_2048.pem new file mode 100644 index 00000000..5889d045 --- /dev/null +++ b/tests/utils/crypto/data/certs_and_keys/k0_cert0_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCku4PI1IBXyIh3 +clvkoTWYqWhD93aHDGkboSTcqnkQQcKN9Dg+oFiZJ8h4NIlni5R+f2ayETeizOrp +h/A41ZT2Zcg+a+ts72BotAlTwNngBE4RGJkB2hk0PtpAKjAAf8bXrmTTXQDz4Bg0 +zteNPjeh/Fuwqs6BmrQjQUcnNVHd+pTh49Ev7bl5vIzFdsJqrotk6c4aQnFqtsO0 +Zto5CG7wEanuRYrOMhBcw0XqqdzpO+1vbXgk3+o7ioBgME1Pnfe1ce6kBh5l/fyv +8KdOttAdlqqzPrjDZArQr9iAW7ZzSocSZPxsmC2V2tbHXrbH1uVT/H88yf6UFHYQ +A2X17Hl/AgMBAAECggEAbB/IOCGCvBubtwsQ1dgaXcGT9kiPO8UhmEkE8PHT1J/V +G2eZI0IL5Tr/kiapqZUsOntU5Lv4UJs/9ViMjEFkLPZRoOck97OHDDJfjOGgIDGz +K/WBOH323RwEFOmb6Df2Q8rr0u/QmEIWoVLCmKqlyWTiqery8I6ifiFymoGc4p1w +6Y6+PkGjFxrK93EIIx1Jp9iz//tHooPQ8R9uU9A53KutsQe0MquYTfZi92rTwQvr +BhuPIpBN7yDZP244/o31NOzQjfomNlluGjnPLmNofJE8zi8SJfoGn0/f95safCRQ +nQuQUcOptOrVVGA9vKrmRvrFbgHU4DRWscNYrNp+AQKBgQDZO6I+hJOEgMkGnrSm +SUifuZl+e1pZAyRscx2bJhKIEjoayWVJUC8e3usl5E44eZnHVGbscE9mp085p/AX +jmvCXy6Ccemz4V9XZ4eBf7cFOFm1RcE2P6B5ZCsvT15Y6GrsoEnJ5oUk99nkzAq8 +zw1STawWTNIkAjyzBsgTuqAHnwKBgQDCIWDOcCBB3tNsJJeMg3FdwqDwljLAGc28 +35Z5L9IpEC4+Rh+YCI7ye2yFQdYTrZOCH79WWHEu+qX37Fbq4g2rQBCjN21gR2Un +VQcoVKxWrdtx4lmoSYMHZNymGYwWm5d+QJ1Iudm86d4Ime6aDkF8ov41XvOxzaJN +Wt79lFvCIQKBgQCIDpCcpX6bc+n0inxM1gN1ftKDZJD+xTgP8L2vSdY7gWcBFfip +RV5t8GLJNchEGO1W6icYmXMxsUKust9ucZZOhDzmGKCuOE71uHMnia1AyL1vCsRr +zMgen71ogUZvWwp1MCNnIEluEQpZAe8LuIb4cIuC4BSR1xDbdDjmGnJWswKBgGbB +Bh2fCePzztLB95l/hYUMXOWbitdVkSm060/P+RyVHPUHZvexKAC/RayvMWIPETHi +HgPVImusbibxaPxAlN2dNnE+CF3azHbqMbSuRN5IfgwktDI4XuuN/qDIivb4elJw +XxA8lzzASS8iU0Il45HWMFoNnU3yu0LYo4lzerIhAoGAdc7Op5SNGJdzLo6ynUab +HAIzaYcEw2uUU+GGjNnzflxko0tTcbj35D/xtXBMG036JwfbK5t6A1JLE1XPt2op +kzFj/QN4CyUG7Twjt+CKGmSgPb+qYZ6IKjoiOOh1bN9ozdjbaf6cgK6Nwrwgfmu7 +VJhEBdSKjs9m0yMF8YIVSs4= +-----END PRIVATE KEY----- diff --git a/tests/utils/crypto/data/certs_and_keys/root_k0_signed_cert0_noca.der.cert b/tests/utils/crypto/data/certs_and_keys/root_k0_signed_cert0_noca.der.cert new file mode 100644 index 0000000000000000000000000000000000000000..6bc01ebc68512d7358aef6f0e09778c6949c9e1c GIT binary patch literal 1117 zcmXqLVu>_pVs>1>%*4pV#9?!ofnnFKd$$aD**LY@JlekVGBR?rG8i;=7;+nMvN4CU zun99c`@uLIJWO7}Fb+EpQ*e|ap8*d@85a+$uXAW{xFL@L7l_Zz!yM%AYbapA2jVgF zu;*12=q2ap8tNP9!mZ+D6jR8~OiId0jW0DG5~T7pxnXa)FwtH+9pBs zMQ2{UZ2w?!b;`HY6L#6JbKWOpY~c((aPt9+pPfNaAG*RmwMA0)u$5+l+VxkD%eOQ_pVs>1>%*4pV#9?!ofnnFKd$$aD**LY@JlekVGBR?rG8i;=7;+nMvN4CU zun99c`@uLIJWO7}Fb+EpQ*e|ap8*d@85a+$uXAW{xFL@L7l_Zz!yM%AYbapA2jVgF zu;*12=q2ap8tNP9!mZ+D6jR8~OiId0jW0IWWMEv}c*LObph4p@V8W19W@(&f&^UWxW8ad-?m-llO^BQU$_28*EKCLr z2HYSfKMM;p6Vn&uhz8~uU_>)AY(JU0*kD;zrs?z>4Luca|6F(KHl7!@ymw00Rh5?A zQAdTIo)B)*teC2i`0d~Q&r&X0c5QPwlx&O&46Cy1<6o>@acNh;>{Zey&wbR&*mPx0 zRL0-DGbi_5y>~XExFU!Np-l2Ccy_p-GJ>QFc$BOu6TIom6rAwUVw@+{hexq2B zaQA1CSN9huOQS@=Yi_rXM#r7o*Q55`FJ!Y%{eiSqXD;TK{OlGH+FHipxW(ko`X$Rn zrkG7WDd72$oBP7T9U(utZcbMhTlam%#}%7S#I0nvRSM#p^rrT_>!dKQUAK3hJQ&>+ kX#60g*ROf`!_5sFH2f7K{EmIz*{*&1)5}G-LP9410MI3h@&Et; literal 0 HcmV?d00001 diff --git a/tests/utils/crypto/test_cert_blocks.py b/tests/utils/crypto/test_cert_blocks.py index eb542e99..c6653d5a 100644 --- a/tests/utils/crypto/test_cert_blocks.py +++ b/tests/utils/crypto/test_cert_blocks.py @@ -11,10 +11,13 @@ from spsdk import SPSDKError from spsdk.exceptions import SPSDKValueError -from spsdk.utils.crypto import CertBlockV2, Certificate from spsdk.utils.crypto.cert_blocks import ( CertBlockHeader, + CertBlockV2, + Certificate, CertificateBlockHeader, + find_main_cert_index, + find_root_certificates, get_main_cert_index, ) @@ -165,24 +168,175 @@ def test_cert_block_invalid(): @pytest.mark.parametrize( - "config,default,passed,expected_result", + "config,passed,expected_result", [ - ({}, None, False, SPSDKError), - ({}, 0, True, 0), - ({"mainRootCertId": 1}, 0, True, 1), - ({"mainCertChainId": 1}, 0, True, 1), - ({"mainRootCertId": "2"}, 0, True, 2), - ({"mainCertChainId": "2"}, 0, True, 2), - ({"mainRootCertId": "1abc"}, 0, False, SPSDKValueError), - ({"mainRootCertId": "1abc"}, 0, False, SPSDKValueError), - ({"mainRootCertId": 1, "mainCertChainId": 1}, 0, True, 1), - ({"mainRootCertId": 1, "mainCertChainId": 2}, 0, False, SPSDKError), + ({}, False, SPSDKError), + ( + { + "mainCertPrivateKeyFile": "k0_cert0_2048.pem", + "rootCertificate0File": "root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k1_signed_cert0_noca.der.cert", + }, + True, + 0, + ), + ( + { + "mainCertPrivateKeyFile": "k0_cert0_2048.pem", + "rootCertificate0File": "root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k1_signed_cert0_noca.der.cert", + "mainRootCertId": 1, + }, + True, + 1, + ), + ({"mainRootCertId": 1}, True, 1), + ({"mainCertChainId": 1}, True, 1), + ({"mainRootCertId": "2"}, True, 2), + ({"mainCertChainId": "2"}, True, 2), + ({"mainRootCertId": "1abc"}, False, SPSDKValueError), + ({"mainRootCertId": "1abc"}, False, SPSDKValueError), + ({"mainRootCertId": 1, "mainCertChainId": 1}, True, 1), + ({"mainRootCertId": 1, "mainCertChainId": 2}, False, SPSDKError), ], ) -def test_get_main_cert_index(config, default, passed, expected_result): +def test_get_main_cert_index(data_dir, config, passed, expected_result): + search_paths = [os.path.join(data_dir, "certs_and_keys")] if passed: - result = get_main_cert_index(config, default) + result = get_main_cert_index(config, search_paths=search_paths) assert result == expected_result else: with pytest.raises(expected_result): - get_main_cert_index(config, default) + get_main_cert_index(config, search_paths=search_paths) + + +@pytest.mark.parametrize( + "config,index", + [ + ( + { + "mainCertPrivateKeyFile": "k0_cert0_2048.pem", + "rootCertificate0File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k0_signed_cert0_noca.der.cert", + "rootCertificate2File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate3File": "root_k1_signed_cert0_noca.der.cert", + }, + 1, + ), + ( + { + "mainCertPrivateKeyFile": "k0_cert0_2048.pem", + "rootCertificate0File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate3File": "root_k1_signed_cert0_noca.der.cert", + }, + None, + ), + ( + { + "rootCertificate0File": "root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": "root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": "root_k3_signed_cert0_noca.der.cert", + }, + None, + ), + ( + { + "mainCertPrivateKeyFile": "k0_cert0_2048.pem", + "rootCertificate0File": "non_existing.cert", + "rootCertificate1File": "another_non_existing.cert", + "rootCertificate2File": "one_more_non_existing.cert", + }, + None, + ), + ( + { + "mainCertPrivateKeyFile": "non_existing.pem", + "rootCertificate0File": "root_k0_signed_cert0_noca.der.cert", + "rootCertificate1File": "root_k1_signed_cert0_noca.der.cert", + "rootCertificate2File": "root_k2_signed_cert0_noca.der.cert", + "rootCertificate3File": "root_k3_signed_cert0_noca.der.cert", + }, + None, + ), + ], +) +def test_find_main_cert_index(data_dir, config, index): + search_paths = [os.path.join(data_dir, "certs_and_keys")] + found_index = find_main_cert_index(config, search_paths=search_paths) + assert found_index == index + + +@pytest.mark.parametrize( + "config,error,expected_list", + [ + ( + { + "rootCertificate0File": "root_k0.cert", + "rootCertificate1File": "root_k1.cert", + "rootCertificate2File": "root_k2.cert", + "rootCertificate3File": "root_k3.cert", + }, + None, + [ + "root_k0.cert", + "root_k1.cert", + "root_k2.cert", + "root_k3.cert", + ], + ), + ( + { + "rootCertificate0File": "root_k0.cert", + "rootCertificate1File": "root_k1.cert", + "rootCertificate2File": "root_k2.cert", + "rootCertificate3File": "", + }, + None, + [ + "root_k0.cert", + "root_k1.cert", + "root_k2.cert", + ], + ), + ( + { + "rootCertificate0File": "root_k0.cert", + "rootCertificate1File": "root_k1.cert", + "rootCertificate2File": "", + "rootCertificate3File": "root_k2.cert", + }, + SPSDKError, + None, + ), + ( + { + "rootCertificate0File": "root_k0.cert", + "rootCertificate3File": "root_k2.cert", + }, + SPSDKError, + None, + ), + ( + { + "rootCertificate0File": "root_k0.cert", + "rootCertificate1File": "root_k1.cert", + }, + None, + [ + "root_k0.cert", + "root_k1.cert", + ], + ), + ], +) +def test_find_root_certficates(config, error, expected_list): + if error is not None: + with pytest.raises(error): + find_root_certificates(config) + else: + certificates = find_root_certificates(config) + assert certificates == expected_list + assert certificates == expected_list diff --git a/tests/utils/crypto/test_common.py b/tests/utils/crypto/test_common.py index bc6c99fd..83650132 100644 --- a/tests/utils/crypto/test_common.py +++ b/tests/utils/crypto/test_common.py @@ -74,7 +74,6 @@ def test_ecc_public_numbers_to_bytes(): @pytest.mark.parametrize("length", [(2048), (4096)]) def test_matching_keys_rsa(length): - prv_keys = [] pub_keys = [] for i in range(4): @@ -87,7 +86,6 @@ def test_matching_keys_rsa(length): @pytest.mark.parametrize("curve", [(curve_name) for curve_name in CurveName]) def test_matching_keys_ecc(curve): - prv_keys = [] pub_keys = [] for i in range(4): @@ -99,7 +97,6 @@ def test_matching_keys_ecc(curve): def test_matching_keys_unmatch(): - prv_keys = [] pub_keys = [] for i in range(4): diff --git a/tests/utils/data/bad_format.xml b/tests/utils/data/bad_format.xml index 9217def9..b459c07e 100644 --- a/tests/utils/data/bad_format.xml +++ b/tests/utils/data/bad_format.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/data/database.yaml b/tests/utils/data/database.yaml index 60a75a03..1c72dc6f 100644 --- a/tests/utils/data/database.yaml +++ b/tests/utils/data/database.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55s6x: revisions: diff --git a/tests/utils/data/database_invalid.yaml b/tests/utils/data/database_invalid.yaml index 560e7579..0f07cb07 100644 --- a/tests/utils/data/database_invalid.yaml +++ b/tests/utils/data/database_invalid.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + devices: lpc55s6x: revisions: diff --git a/tests/utils/data/group_none_reg.yml b/tests/utils/data/group_none_reg.yml index 25e59f28..c81b73a7 100644 --- a/tests/utils/data/group_none_reg.yml +++ b/tests/utils/data/group_none_reg.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + TestRegA0: # Reg Description: TestRegA0 Description value: '0x01020304' # The value width: 32b diff --git a/tests/utils/data/group_reg.yml b/tests/utils/data/group_reg.yml index 6600340e..ae60562f 100644 --- a/tests/utils/data/group_reg.yml +++ b/tests/utils/data/group_reg.yml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + 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 index 168cb76a..8c49da73 100644 --- a/tests/utils/data/grp_regs.xml +++ b/tests/utils/data/grp_regs.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/data/images/image_0x80002000.elf b/tests/utils/data/images/image_0x80002000.elf new file mode 100644 index 0000000000000000000000000000000000000000..aefa7b56d1be6e36a0ce67cfa78afbfe76f6adc9 GIT binary patch literal 1581984 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�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%0AOIBvn4yAVtTDcA|_-) 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%0AA-a_$Bf>w-54;j=a0j{Gi{pX8Z^e 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

9D|Pfz5ku6zB_WCt6K&g=(jTR*@0BnnSG{olN= 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 literal 0 HcmV?d00001 diff --git a/tests/utils/data/registers.xml b/tests/utils/data/registers.xml index 459a664b..759dfe5a 100644 --- a/tests/utils/data/registers.xml +++ b/tests/utils/data/registers.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/data/registers_corr.xml b/tests/utils/data/registers_corr.xml index e837897e..2a7a6c60 100644 --- a/tests/utils/data/registers_corr.xml +++ b/tests/utils/data/registers_corr.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/data/registers_corr2.xml b/tests/utils/data/registers_corr2.xml index dbe79a39..671026f6 100644 --- a/tests/utils/data/registers_corr2.xml +++ b/tests/utils/data/registers_corr2.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/data/registers_reserved.xml b/tests/utils/data/registers_reserved.xml index 646bdd11..31b703ac 100644 --- a/tests/utils/data/registers_reserved.xml +++ b/tests/utils/data/registers_reserved.xml @@ -1,4 +1,9 @@ + diff --git a/tests/utils/test_binary_image.py b/tests/utils/test_binary_image.py index da285428..ba486427 100644 --- a/tests/utils/test_binary_image.py +++ b/tests/utils/test_binary_image.py @@ -1,13 +1,15 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2022 NXP +# Copyright 2022-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause +import os + import pytest -from spsdk.exceptions import SPSDKValueError +from spsdk.exceptions import SPSDKError, SPSDKValueError from spsdk.utils.images import BinaryImage, BinaryPattern @@ -93,3 +95,43 @@ def test_binary_image_draw_invalid(): image.add_image(image_0x4) assert "\x1b[31m" in image.draw() + + +@pytest.mark.parametrize( + "path", + [ + ("images/image.bin"), + ("images/image.hex"), + ("images/image.s19"), + ("images/image.srec"), + ], +) +def test_load_binary_image(path, data_dir): + binary = BinaryImage.load_binary_image(os.path.join(data_dir, path)) + assert binary + assert isinstance(binary, BinaryImage) + assert len(binary) > 0 + + +@pytest.mark.parametrize( + "path, error_msg", + [ + ( + "images/image_corrupted.s19", + "SPSDK: Error loading file: expected crc 'D3' in record S21407F41001020100010600000200000000000000D4, but got 'D4'", + ), + ("invalid_file", "Error loading file"), + ], +) +def test_load_binary_image_invalid(path, error_msg, data_dir): + with pytest.raises(SPSDKError, match=error_msg): + BinaryImage.load_binary_image(os.path.join(data_dir, path)) + + +def test_binary_image_load_elf(data_dir): + """Very simple load of problematic ELF file.""" + binary = BinaryImage.load_binary_image(os.path.join(data_dir, "images/image_0x80002000.elf")) + assert binary + assert isinstance(binary, BinaryImage) + assert len(binary) > 0 + assert binary.offset == 0x8000_2000 diff --git a/tests/utils/test_database.py b/tests/utils/test_database.py index 0b3ab727..08c5e4f8 100644 --- a/tests/utils/test_database.py +++ b/tests/utils/test_database.py @@ -10,7 +10,7 @@ import pytest -from spsdk.exceptions import SPSDKTypeError, SPSDKValueError +from spsdk.exceptions import SPSDKError, SPSDKTypeError, SPSDKValueError from spsdk.utils.database import Database, Devices, Revisions from spsdk.utils.misc import load_configuration @@ -60,7 +60,7 @@ def test_invalid_revision(data_dir): def test_loading_invalid_database(data_dir): db_path = os.path.join(data_dir, "database_invalid.yaml") - with pytest.raises(SPSDKTypeError): + with pytest.raises(SPSDKError): Database(db_path) diff --git a/tests/utils/test_devicedescription.py b/tests/utils/test_devicedescription.py index 8ef116fc..a9f3b1af 100644 --- a/tests/utils/test_devicedescription.py +++ b/tests/utils/test_devicedescription.py @@ -8,7 +8,6 @@ from unittest.mock import MagicMock, patch import pytest -from libusbsio import LIBUSBSIO import spsdk.utils.devicedescription as devicedescription from spsdk.mboot.interfaces.usb import USB_DEVICES as MB_USB_DEVICES diff --git a/tests/utils/test_misc.py b/tests/utils/test_misc.py index 19273189..79cc9dc9 100644 --- a/tests/utils/test_misc.py +++ b/tests/utils/test_misc.py @@ -15,7 +15,6 @@ from spsdk import SPSDKError from spsdk.exceptions import SPSDKValueError from spsdk.utils.exceptions import SPSDKTimeoutError -from spsdk.utils.images import BinaryImage from spsdk.utils.misc import ( BinaryPattern, Timeout, @@ -470,37 +469,6 @@ def test_size_format(input_value, use_kibibyte, expected): assert size_fmt(input_value, use_kibibyte) == expected -@pytest.mark.parametrize( - "path", - [ - ("images/image.bin"), - ("images/image.hex"), - ("images/image.s19"), - ("images/image.srec"), - ], -) -def test_load_binary_image(path, data_dir): - binary = BinaryImage.load_binary_image(os.path.join(data_dir, path)) - assert binary - assert isinstance(binary, BinaryImage) - assert len(binary) > 0 - - -@pytest.mark.parametrize( - "path, error_msg", - [ - ( - "images/image_corrupted.s19", - "SPSDK: Error loading file: expected crc 'D3' in record S21407F41001020100010600000200000000000000D4, but got 'D4'", - ), - ("invalid_file", "Error loading file"), - ], -) -def test_load_binary_image_invalid(path, error_msg, data_dir): - with pytest.raises(SPSDKError, match=error_msg): - BinaryImage.load_binary_image(os.path.join(data_dir, path)) - - def test_swap16_invalid(): with pytest.raises(SPSDKError, match="Incorrect number to be swapped"): swap16(0xFFFFA) diff --git a/tests/utils/test_nxpdevscan.py b/tests/utils/test_nxpdevscan.py index b53a83dc..679f2e9c 100644 --- a/tests/utils/test_nxpdevscan.py +++ b/tests/utils/test_nxpdevscan.py @@ -273,15 +273,17 @@ def get_return(path): "Release number: 125" ) - with patch("platform.system", MagicMock(return_value="Windows")): - devices = nds.search_libusbsio_devices() - assert len(devices) == 1 - assert devices[0].info() == get_return(PATH_BY_SYSTEM["win"]) - - with patch("platform.system", MagicMock(return_value="Linux")): - devices = nds.search_libusbsio_devices() - assert len(devices) == 1 - assert devices[0].info() == get_return(PATH_BY_SYSTEM["linux"]) + if platform.system() != "Darwin": + # Windows and Linux libraries cannot be loaded on Mac with Apple Sillicon + with patch("platform.system", MagicMock(return_value="Windows")): + devices = nds.search_libusbsio_devices() + assert len(devices) == 1 + assert devices[0].info() == get_return(PATH_BY_SYSTEM["win"]) + + with patch("platform.system", MagicMock(return_value="Linux")): + devices = nds.search_libusbsio_devices() + assert len(devices) == 1 + assert devices[0].info() == get_return(PATH_BY_SYSTEM["linux"]) with patch("platform.system", MagicMock(return_value="Darwin")): devices = nds.search_libusbsio_devices() diff --git a/tests/utils/test_schema_validator.py b/tests/utils/test_schema_validator.py index 173277a4..11039626 100644 --- a/tests/utils/test_schema_validator.py +++ b/tests/utils/test_schema_validator.py @@ -6,15 +6,124 @@ # SPDX-License-Identifier: BSD-3-Clause import os +from typing import Any, Dict, Optional import pytest import yaml -from attr import validate from spsdk import SPSDKError from spsdk.image import TZ_SCH_FILE from spsdk.utils.misc import use_working_directory -from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas, check_config +from spsdk.utils.schema_validator import CommentedConfig, ValidationSchemas, check_config + +# schema for testing commented YAML configuration +_TEST_CONFIG_SCHEMA = { + "type": "object", + "title": "main_title", + "description": "main_description", + "properties": { + "n1": { + "type": "bool", + "title": "n1_title", + "description": "n1_description", + "template_value": "false", + }, + "n2": { + "type": "string", + "title": "n2_title", + "description": "n2_description", + "template_value": "n2_value", + }, + "n3": { + "type": "string", + "title": "n3_title", + "description": "n3_description", + "template_value": "n3_value", + }, + "n4": { + "type": "string", + "title": "n4_title", + "description": "n4_description", + "skip_in_template": True, + }, + "n5": { + "type": "string", + "title": "n5_title", + "description": "n5_description", + "template_value": "n5_value", + }, + "n6": { + "type": "string", + "title": "n6_title", + "description": "n6_description", + "template_value": "n6_value", + }, + "arr": { + "type": "array", + "title": "arr_title", + "description": "arr description", + "items": { + "oneOf": [ + { + "type": "object", + "required": ["itm1"], + "properties": { + "itm1": { + "type": ["string", "number"], + "title": "itm1_title", + "description": "itm1 description", + "format": "number", + "template_value": "0x0", + } + }, + }, + { + "type": "object", + "required": ["itm2"], + "properties": { + "itm2": { + "type": ["string", "number"], + "title": "itm2_title", + "description": "itm2 description", + "format": "number", + "template_value": "0x1", + } + }, + }, + ] + }, + }, + }, + "required": ["n2", "arr"], + "if": {"properties": {"n1": "true"}}, + "then": {"required": ["n3"]}, + "anyOf": [{"required": ["n5"]}, {"required": ["n6"]}], +} + + +# tested configuration +_TEST_CONFIG = { + "n1": False, + "n2": "test value #2", + "n5": "test value", + "arr": [{"itm1": 1}, {"itm1": 2}, {"itm2": "0x3"}], +} + +# expected commented output for the tested configuration +_EXP_CONFIG_RESULT = ( + "# =========== Super Main Title ===========\n" + "# ----------------------------------------------------------------------------------------------------\n" + "# == main_title == \n" + "# main_description \n" + "# ----------------------------------------------------------------------------------------------------\n" + "n1: false # [Optional], n1_title; n1_description\n" + "n2: 'test value #2' # [Required], n2_title; n2_description\n" + "n5: test value # [Conditionally required], n5_title; n5_description\n" + "arr: # [Required], arr_title; arr description\n" + " - itm1: 1 # [Required], itm1_title; itm1 description\n" + " - itm1: 2 # [Required], itm1_title; itm1 description\n" + " - itm2: '0x3' # [Required], itm2_title; itm2 description\n" +) @pytest.mark.parametrize( @@ -37,7 +146,7 @@ ({"d1": "testdir_invalid"}, False), ], ) -def test_schema_validator(tmpdir, test_vector, result): +def test_schema_validator(tmpdir, test_vector, result) -> None: """Basic test of scheme validator.""" schema = { "type": "object", @@ -71,7 +180,7 @@ def test_schema_validator(tmpdir, test_vector, result): ({"n2": "Hello"}, False), ], ) -def test_schema_validator_required(test_vector, result): +def test_schema_validator_required(test_vector, result) -> None: """Basic test of scheme validator.""" schema = { "type": "object", @@ -89,7 +198,7 @@ def test_schema_validator_required(test_vector, result): check_config(test_vector, [schema]) -def test_schema_invalid_validator(): +def test_schema_invalid_validator() -> None: """Basic test of scheme validator.""" schema = { "type": "object", @@ -113,57 +222,12 @@ def _is_yaml_comment(yaml_data: str, comment: str, key: str = None) -> bool: return False -def test_config_template(): - - schema = { - "type": "object", - "title": "main_title", - "description": "main_description", - "properties": { - "n1": { - "type": "bool", - "title": "n1_title", - "description": "n1_description", - "template_value": "false", - }, - "n2": { - "type": "string", - "title": "n2_title", - "description": "n2_description", - "template_value": "n2_value", - }, - "n3": { - "type": "string", - "title": "n3_title", - "description": "n3_description", - "template_value": "n3_value", - }, - "n4": { - "type": "string", - "title": "n4_title", - "description": "n4_description", - "skip_in_template": True, - }, - "n5": { - "type": "string", - "title": "n5_title", - "description": "n5_description", - "template_value": "n5_value", - }, - "n6": { - "type": "string", - "title": "n6_title", - "description": "n6_description", - "template_value": "n6_value", - }, - }, - "required": ["n2"], - "if": {"properties": {"n1": "true"}}, - "then": {"required": ["n3"]}, - "anyOf": [{"required": ["n5"]}, {"required": ["n6"]}], - } - - my_yml_template = ConfigTemplate("Super Main Title", [schema]).export_to_yaml() +@pytest.mark.parametrize("override_values", [None, {"n2": "override"}]) +def test_config_template(override_values: Optional[Dict[str, Any]]) -> None: + """Test export of commented configuration template""" + my_yml_template = CommentedConfig( + "Super Main Title", [_TEST_CONFIG_SCHEMA], values=override_values, export_template=True + ).export_to_yaml() assert _is_yaml_comment(my_yml_template, "main_description") assert _is_yaml_comment(my_yml_template, "n1_description", "n1") @@ -176,9 +240,22 @@ def test_config_template(): assert "n4" not in yaml_config assert _is_yaml_comment(my_yml_template, "[Conditionally required]", "n5") assert _is_yaml_comment(my_yml_template, "[Conditionally required]", "n6") + assert _is_yaml_comment(my_yml_template, "[Required]", "arr") + assert _is_yaml_comment(my_yml_template, "[Required]", "itm1") + assert _is_yaml_comment(my_yml_template, "[Required]", "itm2") + + +def test_config() -> None: + """Test export of custom commented configuration""" + c_cfg = CommentedConfig( + "Super Main Title", [_TEST_CONFIG_SCHEMA], _TEST_CONFIG, export_template=False + ) + yml_cfg_str = c_cfg.export_to_yaml() + + assert yml_cfg_str == _EXP_CONFIG_RESULT -def test_validate_oneof(): +def test_validate_oneof() -> None: schema = { "type": "array", "items": { @@ -202,7 +279,7 @@ def test_validate_oneof(): check_config(test_vector, [schema]) -def test_load_schema_file(): +def test_load_schema_file() -> None: """Test class ValidationSchemas""" assert isinstance(ValidationSchemas.get_schema_file(TZ_SCH_FILE), dict) diff --git a/tests/utils/test_usbfilter.py b/tests/utils/test_usbfilter.py index d9e731d2..a8bc42a9 100644 --- a/tests/utils/test_usbfilter.py +++ b/tests/utils/test_usbfilter.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- # -# Copyright 2021-2022 NXP +# Copyright 2021-2023 NXP # # SPDX-License-Identifier: BSD-3-Clause from unittest.mock import MagicMock, patch @@ -234,7 +234,6 @@ def test_device_name_linux(filter_usb_id: str, vid: str, pid: str, path: str, ex [("MKL27", "MKL27", True), ("Nonsense", "MKL27", False)], ) def test_general_device_name(filter_usb_id: str, name: str, expected: bool): - usb_filter = USBDeviceFilter(usb_id=filter_usb_id) g_virtual_hid_device = {"device_name": name} @@ -246,7 +245,6 @@ def test_general_device_name(filter_usb_id: str, name: str, expected: bool): [("1234567", "1234567", True), ("Nonsense", "1234567", False)], ) def test_general_device_serial_number(filter_usb_id: str, serial: str, expected: bool): - usb_filter = USBDeviceFilter(usb_id=filter_usb_id) g_virtual_hid_device = {"serial_number": serial} diff --git a/tools/approved_packages.json b/tools/approved_packages.json index 730158cb..d4ef4c85 100644 --- a/tools/approved_packages.json +++ b/tools/approved_packages.json @@ -1,6 +1,7 @@ { "licenses": [ "Apache 2.0", + "Apache-2.0", "Apache-2.0 license", "Apache License 2.0", "Apache License, Version 2.0", @@ -215,12 +216,24 @@ "home_page": "UNKNOWN", "is_manual": false }, + { + "name": "libusb-package", + "license": "Apache 2.0", + "home_page": "https://github.com/pyocd/libusb-package", + "is_manual": true + }, { "name": "milksnake", "license": "Apache License 2.0", "home_page": "https://github.com/getsentry/milksnake", "is_manual": false }, + { + "name": "natsort", + "license": "MIT", + "home_page": "https://github.com/SethMMorton/natsort", + "is_manual": true + }, { "name": "naturalsort", "license": "MIT", @@ -396,4 +409,4 @@ "is_manual": false } ] -} +} \ No newline at end of file diff --git a/tools/checker_copyright_year.py b/tools/checker_copyright_year.py index bed8e10b..a81e288c 100644 --- a/tools/checker_copyright_year.py +++ b/tools/checker_copyright_year.py @@ -11,45 +11,167 @@ import os import re import sys -from typing import Optional, Sequence +from typing import List, Optional, Sequence -EXTENSIONS = [".py"] -COPYRIGHT_REGEX_STR = r"Copyright.*(?P\d{4}) (?P.*)" +COPYRIGHT_EXTENSIONS = ["py", "yml", "yaml", "xml"] +COPYRIGHT_REGEX_STR = r"Copyright.*?(?P\d{4})?-?(?P\d{4}) (?P.*)" COPYRIGHT_REGEX = re.compile(COPYRIGHT_REGEX_STR) +NXP_HOLDER_NAME = "NXP" THIS_YEAR = datetime.datetime.now().year -EXCLUDED_FILES = ["docs/conf.py"] +EXCLUDED_FILES = ["docs/conf.py", ".pre-commit-config.yaml"] -def check_file(file: str) -> int: +def check_file(file: str, silent: bool = False) -> int: """Run the check on single file.""" ret_val = 0 if not os.path.isfile(file): - print(f"'{file}' doesn't exist anymore") + if not silent: + print(f"'{file}' doesn't exist anymore") return 0 with open(file) as f: content = f.read() copyrights = COPYRIGHT_REGEX.findall(content) for cp_instance in copyrights: - cp_year = int(cp_instance[0]) - if cp_year == THIS_YEAR: + till_year = int(cp_instance[1]) + if till_year == THIS_YEAR: break else: - print(f"File: '{file}' doesn't have {THIS_YEAR} Copyright") + if not silent: + print(f"File: '{file}' doesn't have {THIS_YEAR} Copyright") ret_val = 1 return ret_val +def fix_file(file: str) -> None: + """Fix copyright on single file.""" + result = check_file(file, silent=True) + if result: + with open(file) as f: + content = f.read() + if len(COPYRIGHT_REGEX.findall(content)) > 0: + fixed_content = update_copyright(content) + else: + ext = os.path.splitext(file)[1][1:] + fixed_content = add_copyright(content, ext) + with open(file, "w") as f: + f.write(fixed_content) + + +def update_copyright(content: str) -> str: + """Update copyright comment section of an existing file content.""" + copyrights = COPYRIGHT_REGEX.findall(content) + for cp_instance in copyrights: + if cp_instance[2] != NXP_HOLDER_NAME: + continue + from_year = int(cp_instance[0]) if cp_instance[0] else None + till_year = int(cp_instance[1]) if cp_instance[1] else None + assert till_year + if not from_year: + from_year = till_year + till_year = THIS_YEAR + year_string = generate_copyright_year_clause(till_year=till_year, from_year=from_year) + nxp_copyright_regex_str = COPYRIGHT_REGEX_STR.replace( + COPYRIGHT_REGEX_STR.split(" ")[-1], NXP_HOLDER_NAME + ) + content = re.sub(nxp_copyright_regex_str, year_string, content) + return content + + +def add_copyright(content: str, extension: str) -> str: + """Add copyright comment section to an existing file content.""" + lines = content.splitlines(keepends=True) + index = find_copyright_line_index(lines, extension) + # Add one more empty line between comment sections + leading_line = True if extension in ["py", "yml", "yaml"] and index != 0 else False + + comment = generate_copyright_comment( + file_extension=extension, till_year=THIS_YEAR, leading_empty_line=leading_line + ) + for i in range(len(comment)): + lines.insert(index + i, comment[i] + "\n") + fixed_content = "".join(lines) + return fixed_content + + +def find_copyright_line_index(lines: list, file_extension: str) -> int: + """Find the index of line in source code, where copyright text should be placed.""" + if file_extension in ["yml", "yaml"]: + return 0 + elif file_extension == "py": + index = 0 + for i, s in enumerate(lines): + if not s.startswith("#"): + index = i + break + return index + elif file_extension == "xml": + return 1 if re.match(r"<\?xml version.*\?>", lines[0]) else 0 + else: + raise TypeError(f"Unsupported file type {file_extension}") + + +def comment_text(text_lines: list, file_extension: str) -> list: + """Build a commented text from list of text lines.""" + if file_extension in ["py", "yml", "yaml"]: + for i, s in enumerate(text_lines): + text_lines[i] = f"# {s}" if s else "#" + elif file_extension in ["xml"]: + for i, s in enumerate(text_lines): + text_lines[i] = f" {s}" + text_lines.insert(0, "") + else: + raise TypeError(f"Unsupported file type {file_extension}") + return text_lines + + +def generate_copyright_comment( + file_extension: str, + till_year: int, + from_year: Optional[int] = None, + leading_empty_line: bool = False, +) -> list: + """Create full copyright text with leading empty line if required.""" + year_string = generate_copyright_year_clause(till_year=till_year, from_year=from_year) + copyright_lines = [year_string, "", "SPDX-License-Identifier: BSD-3-Clause"] + if leading_empty_line: + copyright_lines.insert(0, "") + return comment_text(copyright_lines, file_extension) + + +def generate_copyright_year_clause(till_year: int, from_year: Optional[int] = None) -> str: + """Create copyright sentence with years range.""" + years = f"{from_year}-{till_year}" if from_year else till_year + return f"Copyright {years} {NXP_HOLDER_NAME}" + + +def fix_copyright_in_files(files: Sequence[str]) -> None: + """Fix copyright in list of files.""" + files = filter_files(files) + for file in files: + fix_file(file) + + def check_files(files: Sequence[str]) -> int: """Run the check on a list of files.""" ret_val = 0 + files = filter_files(files) + for file in files: + ret_val += check_file(file) + return ret_val + + +def filter_files(files: Sequence[str]) -> Sequence[str]: + """Filter files to be checked.""" + collected_files: List[str] = [] for file in files: if file in EXCLUDED_FILES: continue - _, extension = os.path.splitext(file) - if extension in EXTENSIONS: - ret_val += check_file(file) - return ret_val + extension = os.path.splitext(file)[1][1:] + if extension in COPYRIGHT_EXTENSIONS: + collected_files.append(file) + return collected_files def main(argv: Optional[Sequence[str]] = None) -> int: @@ -58,9 +180,13 @@ def main(argv: Optional[Sequence[str]] = None) -> int: description="""Check whether "files" have the current year in Copyright.""" ) parser.add_argument("files", nargs="*", help="Files to analyze") + parser.add_argument("--fix", action="store_true", help="Fix the copyright in files") args = parser.parse_args(argv) - return check_files(args.files) + ret_val = check_files(args.files) + if args.fix: + fix_copyright_in_files(args.files) + return ret_val if __name__ == "__main__": diff --git a/tools/checker_dependencies.py b/tools/checker_dependencies.py index 1f2959ca..f83f5e01 100644 --- a/tools/checker_dependencies.py +++ b/tools/checker_dependencies.py @@ -16,6 +16,8 @@ from pip import __version__ as pip_version +from spsdk.exceptions import SPSDKError + APPROVED_LICENSES_FILE_NAME = "approved_packages.json" APPROVED_LICENSES_FILE = os.path.abspath( os.path.join(os.path.dirname(__file__), APPROVED_LICENSES_FILE_NAME) @@ -107,7 +109,7 @@ def get_packages_info(packages: List[str]) -> List[Tuple[DependencyInfo, List[st """Get packages info for list of packages. :param packages: List of packages names. - :raises ValueError: Some of required packages doesn't exists. + :raises SPSDKError: Some of required packages doesn't exists. :return: List of Tuples with DependencyInfo and List of package dependencies. """ packages_info: List[str] = [] @@ -116,7 +118,7 @@ def get_packages_info(packages: List[str]) -> List[Tuple[DependencyInfo, List[st f"pip show {' '.join(packages)}".split(), stderr=subprocess.DEVNULL ).decode("utf-8") if "WARNING: Package(s) not found:" in output: - raise ValueError(f"Some package(s) not found: \n{output}") + raise SPSDKError(f"Some package(s) not found: \n{output}") packages_info = output.split("---") @@ -223,7 +225,6 @@ def check_dependencies(actual_list: DependenciesList) -> int: approved_licenses = DependenciesList.load_licenses(APPROVED_LICENSES_FILE) issues_counter = 0 for actual_dep in actual_list: - lic = actual_dep.license if actual_dep.is_manual: fall_back = approved_list.get(actual_dep.name) diff --git a/tools/checker_py_headers.py b/tools/checker_py_headers.py new file mode 100644 index 00000000..2731ab15 --- /dev/null +++ b/tools/checker_py_headers.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2021-2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Script used during pre-commit to check if changed files have valid copyright year.""" +import argparse +import os +import sys +from typing import List, Optional, Sequence + +EXCLUDED_FILES = ["docs/conf.py"] +DEFAULT_HEADER = ["#!/usr/bin/env python", "# -*- coding: utf-8 -*-"] + + +def check_file(file: str, header_lines: Sequence[str], silent: bool = False) -> int: + """Run the check on single file.""" + if not os.path.isfile(file): + if not silent: + print(f"'{file}' doesn't exist anymore") + return 0 + with open(file) as f: + lines = f.read().splitlines() + for idx, line in enumerate(header_lines): + if lines[idx].lower() != line.lower(): + if not silent: + print(f"File: '{file}' doesn't have valid header") + return 1 + return 0 + + +def fix_file(file: str, header_lines: Sequence[str]) -> None: + """Fix copyright on single file.""" + result = check_file(file, header_lines=header_lines, silent=True) + if result: + with open(file) as f: + content_lines = f.read().splitlines(keepends=True) + requires_empty_comment = ( + content_lines[0].startswith("#") and content_lines[0].strip() != "#" + ) + for index in range(len(header_lines)): + content_lines.insert(index, header_lines[index] + "\n") + # Add empty comment between header and following comment + if requires_empty_comment: + content_lines.insert(len(header_lines), "#\n") + with open(file, "w") as f: + f.write("".join(content_lines)) + + +def fix_py_headers_in_files(files: Sequence[str], header_lines: Sequence[str]) -> None: + """Fix copyright in list of files.""" + files = filter_files(files) + for file in files: + fix_file(file, header_lines) + + +def check_files(files: Sequence[str], header_lines: Sequence[str]) -> int: + """Run the check on a list of files.""" + ret_val = 0 + files = filter_files(files) + for file in files: + ret_val += check_file(file, header_lines) + return ret_val + + +def filter_files(files: Sequence[str]) -> Sequence[str]: + """Filter files to be checked.""" + collected_files: List[str] = [] + for file in files: + if file in EXCLUDED_FILES: + continue + extension = os.path.splitext(file)[1][1:] + if extension == "py": + collected_files.append(file) + return collected_files + + +def main(argv: Optional[Sequence[str]] = None) -> int: + """Check whether py "files" have the correct header.""" + parser = argparse.ArgumentParser( + description="""Check whether py "files" have the correct header.""" + ) + parser.add_argument("--fix", action="store_true", help="Fix the headers in files") + parser.add_argument("--header", required=False, help="Header to be tested") + parser.add_argument("files", nargs="*", help="Files to analyze") + args = parser.parse_args(argv) + + if not args.header: + header_lines = DEFAULT_HEADER + else: + header_lines = args.header.split("\n") + if args.fix: + fix_py_headers_in_files(args.files, header_lines) + ret_val = 0 + else: + ret_val = check_files(args.files, header_lines) + sys.exit(ret_val) + + +if __name__ == "__main__": + main() diff --git a/tools/fcb_header_to_xml.py b/tools/fcb_header_to_xml.py index 59d39987..4bb21959 100644 --- a/tools/fcb_header_to_xml.py +++ b/tools/fcb_header_to_xml.py @@ -27,6 +27,7 @@ def get_struct(text: str, name: str) -> str: :param text_lines: Input lines with header file. :param name: name of structure + :raises SPSDKError: When there is invalid structure name to find :return: Subset of lines with structure content. """ # pylint: disable=anomalous-backslash-in-string # \s is a part of the regular expression @@ -97,7 +98,7 @@ def __next__(self) -> StructMember: ) match = re.match(re_type_name, self.text[self.last_ix :]) if not match: - raise StopIteration + raise SPSDKError() self.last_ix += match.end() mem_type = match.group("mem_type") mem_name = match.group("mem_name") @@ -120,7 +121,6 @@ def __next__(self) -> StructMember: def get_reg(member: StructMember, offset: int, header: str) -> RegsRegister: - if member.is_standard_type(): return RegsRegister( name=member.name, diff --git a/tools/gitcov.py b/tools/gitcov.py index 0202000f..8ddbfc7d 100644 --- a/tools/gitcov.py +++ b/tools/gitcov.py @@ -14,7 +14,7 @@ import sys from configparser import ConfigParser from os import path -from typing import Optional, Sequence, Tuple +from typing import Iterable, Optional, Sequence, Tuple from xml.etree import ElementTree as et logger = logging.getLogger() @@ -156,7 +156,10 @@ def parse_input(input_args: Optional[Sequence[str]] = None) -> argparse.Namespac def get_changed_files( - repo_path: str, include_merges: bool, parent_branch: Optional[str] = None + repo_path: str, + include_merges: bool, + parent_branch: Optional[str] = None, + file_extensions: Iterable[str] = ["py"], ) -> Sequence[str]: """Get a list of changed files. @@ -165,7 +168,8 @@ def get_changed_files( :param include_merges: Include changes done via merge-commits :return: List of changed files """ - file_regex_str = r"^(?P[AM])\s+(?P[a-zA-Z0-9_/\\]+\.py)$" + file_extension_regex = "(" + "|".join(file_extensions) + ")" + file_regex_str = rf"^(?P[AM])\s+(?P[a-zA-Z0-9_/\\]+\.{file_extension_regex})$" file_regex = re.compile(file_regex_str) parent_branch = parent_branch or get_parent_commit() @@ -366,7 +370,6 @@ def get_parent_commit() -> str: ) while 1: - # first check if we're not on a merge commit if current_sha in merge_commits: break diff --git a/tools/release_notes.py b/tools/release_notes.py index ac3a04d5..e3b04494 100644 --- a/tools/release_notes.py +++ b/tools/release_notes.py @@ -21,6 +21,8 @@ from cryptography.hazmat.primitives import hashes from jira import JIRA, Issue +from spsdk.exceptions import SPSDKError + JIRA_SERVER = "https://jira.sw.nxp.com" TICKET_REGEX = re.compile(r"(SPSDK-\d+)") @@ -215,7 +217,7 @@ def get_ticket_info(ticket: str, jira: Optional[JIRA]) -> TicketRecord: The `@cachier` decorator produces persistent cache to alleviate load on JIRA server. """ if not jira: - raise RuntimeError(f"Info for {ticket} is not pre-recorded, can't work in offline mode") + raise SPSDKError(f"Info for {ticket} is not pre-recorded, can't work in offline mode") logging.info(f"Fetching info for ticket: {ticket}") issue = jira.issue(ticket) return TicketRecord.from_jira_issue(issue=issue) diff --git a/tools/req_update.py b/tools/req_update.py index 798f9ecd..12857593 100644 --- a/tools/req_update.py +++ b/tools/req_update.py @@ -20,6 +20,8 @@ import pkg_resources import requests +from spsdk.exceptions import SPSDKError + THIS_DIR = os.path.abspath(os.path.dirname(__file__)) REPO_ROOT = os.path.normpath(os.path.join(THIS_DIR, "..")) IGNORE_LIST = ["cmsis-pack-manager"] @@ -39,7 +41,7 @@ def from_str(selector: Optional[str] = None) -> "NextVersion": return NextVersion.MINOR if selector == "major": return NextVersion.MAJOR - raise ValueError(f"Unknown NextVersion selector '{selector}'. Use 'minor' or 'major'") + raise SPSDKError(f"Unknown NextVersion selector '{selector}'. Use 'minor' or 'major'") @dataclass @@ -75,7 +77,7 @@ def from_req(requirement: pkg_resources.Requirement) -> "RequirementsRecord": def get_next_version(self, selector: NextVersion) -> str: if not self.max_version: - raise ValueError("max_version is not defined") + raise SPSDKError("max_version is not defined") if selector == NextVersion.NONE: return self.max_version version = pkg_resources.parse_version(self.max_version) @@ -83,7 +85,7 @@ def get_next_version(self, selector: NextVersion) -> str: return f"{version.major}.{version.minor + 1}" if selector == NextVersion.MAJOR: return f"{version.major + 1}" - raise ValueError(f"Unknown next version selector: {selector}") + raise SPSDKError(f"Unknown next version selector: {selector}") @staticmethod def from_str(req_line: str) -> "RequirementsRecord": @@ -116,7 +118,7 @@ def get_record(self, name: str) -> RequirementsRecord: for req in self: if req.name == name: return req - raise ValueError(f"Requirement named {name} wasn't found") + raise SPSDKError(f"Requirement named {name} wasn't found") @staticmethod def from_pip() -> "RequirementsList": diff --git a/tools/sample_config_data/tp_config_data.yaml b/tools/sample_config_data/tp_config_data.yaml index a004ce09..6afdf3d2 100644 --- a/tools/sample_config_data/tp_config_data.yaml +++ b/tools/sample_config_data/tp_config_data.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # Path to CMPA binary cmpa_path: sample_config_data/cmpa_example.bin # Path to CFPA binary diff --git a/tools/sample_config_data/tp_config_real_hw.yaml b/tools/sample_config_data/tp_config_real_hw.yaml index 107d9734..10201713 100644 --- a/tools/sample_config_data/tp_config_real_hw.yaml +++ b/tools/sample_config_data/tp_config_real_hw.yaml @@ -1,2 +1,6 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # Path to device family attestation certificate nxp_prod_cert_path: sample_config_data/nxp_prod_devattest_lpc55s6x_hw.der.crt diff --git a/tools/sample_config_data/tp_host_data.yaml b/tools/sample_config_data/tp_host_data.yaml index 77a2fff3..e3a27151 100644 --- a/tools/sample_config_data/tp_host_data.yaml +++ b/tools/sample_config_data/tp_host_data.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # OEM Provisioning firmware provided by NXP (optional) # if omitted, use blhost's receive-sb-file to load prov_fw beforehand prov_firmware: sample_config_data/signed_blinky.sb2 diff --git a/tools/sample_config_data/tp_host_real_hw.yaml b/tools/sample_config_data/tp_host_real_hw.yaml index 0714d4a9..38c6b4ae 100644 --- a/tools/sample_config_data/tp_host_real_hw.yaml +++ b/tools/sample_config_data/tp_host_real_hw.yaml @@ -1,3 +1,7 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: BSD-3-Clause + # OEM Provisioning firmware provided by NXP (optional) # if omitted, use blhost's receive-sb-file to load prov_fw beforehand prov_firmware: diff --git a/tools/sr_xls2xml.py b/tools/sr_xls2xml.py index 2509e834..f7ccd9a0 100644 --- a/tools/sr_xls2xml.py +++ b/tools/sr_xls2xml.py @@ -16,6 +16,7 @@ from openpyxl import utils from openpyxl.utils import cell as cell_utils +from spsdk.exceptions import SPSDKError from spsdk.utils.misc import value_to_int from spsdk.utils.registers import Registers, RegsBitField, RegsEnum, RegsRegister @@ -283,7 +284,7 @@ def _get_bitfields(self, reg: Any, excel_row: int, excel_row_cnt: int) -> None: reg.add_bitfield(bitfield) except Exception as exc: print(f"Error raised during loading bitfield {bitfield_name}. {exc}") - raise ValueError(str(exc)) from exc + raise SPSDKError(str(exc)) from exc 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 @@ -329,7 +330,7 @@ def _get_enums(self, bitfield: Any, excel_row: int, excel_row_cnt: int) -> None: ) except Exception as exc: print(f"Error raised during loading enum {enum_name}. {exc}") - raise ValueError(str(exc)) from exc + raise SPSDKError(str(exc)) from exc def _get_merged_by_first_cell(self, cell: str) -> str: """Function returns the merged range by first cell.""" diff --git a/tools/task_scheduler.py b/tools/task_scheduler.py index 4e133c7c..56c42668 100644 --- a/tools/task_scheduler.py +++ b/tools/task_scheduler.py @@ -16,6 +16,7 @@ import colorama +from spsdk.exceptions import SPSDKError from spsdk.utils.easy_enum import Enum colorama.just_fix_windows_console() @@ -27,7 +28,7 @@ class TaskState(Enum): READY = (0, "Ready") BLOCKED = (1, "Blocked") RUNNING = (2, "Running") - DONE = (3, "Done") + PASSED = (3, "Passed") FAILED = (4, "Failed") @@ -47,13 +48,26 @@ def __init__( method: Callable, *args: Any, dependencies: Optional[List[str]] = None, + inherit_failure: bool = True, + info_only: bool = False, **kwargs: Any, ) -> None: + """Task info initialization. + :param name: Name of the task + :param method: Method to be called in the task + :param args: Argument to be passed into method + :param kwargs: Keyword argument to be passed into method + :param dependencies: Task names the actual task is dependent on + :param inherit_failure: Fail the task immediately if the dependency fails + :param info_only: This is just information task - fault of this task doesn't cause fail result + """ self.name = name self.method = method self.args = args self.kwargs = kwargs self.dependencies = dependencies + self.inherit_failure = inherit_failure + self.info_only = info_only self._state = TaskState.READY self.result: Optional[TaskResult] = None self.exec_start = 0.0 @@ -89,13 +103,16 @@ def status_str(self) -> str: :return: Task state. """ - - return TaskState.name(self._state) + ret = f"{self.get_color_by_status()}{TaskState.name(self._state)}" + if self.info_only: + ret += f"{colorama.Fore.CYAN} [INFO ONLY]" + ret += colorama.Fore.RESET + return ret def start_task(self) -> None: """Start task event.""" if self.is_failed(): - raise IndexError("The task is already failed.") + raise SPSDKError("The task is already failed.") self.exec_start = time.perf_counter() self.status = TaskState.RUNNING @@ -103,11 +120,12 @@ def start_task(self) -> None: def finish_task(self, result: Optional[TaskResult], exc: Optional[BaseException]) -> None: """Finish task event.""" if not self.is_running(): - raise IndexError("The task is not running.") + raise SPSDKError("The task is not running.") exec_stop = time.perf_counter() self.exec_time = exec_stop - self.exec_start - self.status = TaskState.DONE if not exc else TaskState.FAILED + passed = not exc and result.error_count == 0 if result else not exc + self.status = TaskState.PASSED if passed else TaskState.FAILED self.result = result self.exception = exc @@ -116,7 +134,7 @@ def is_finished(self) -> bool: :return: True if task is finished, otherwise False. """ - return self.status in [TaskState.DONE, TaskState.FAILED] + return self.status in [TaskState.PASSED, TaskState.FAILED] def is_failed(self) -> bool: """Get the state if the task is failed. @@ -148,18 +166,25 @@ def get_color_by_status(self) -> colorama.Fore: TaskState.READY: colorama.Fore.WHITE, TaskState.BLOCKED: colorama.Fore.MAGENTA, TaskState.RUNNING: colorama.Fore.YELLOW, - TaskState.DONE: colorama.Fore.GREEN, + TaskState.PASSED: colorama.Fore.GREEN, TaskState.FAILED: colorama.Fore.RED, } return colors[self.status] def get_exec_time(self) -> str: - """GEt execution time in string format. + """Get execution time in string format. :return: Execution time information. """ return str(round(self.exec_time, 1)) + "s" + def reset(self) -> None: + """Reset the task to its initial state.""" + self._state = TaskState.READY + self.result = None + self.exec_start = 0.0 + self.exec_time = 0.0 + # pylint: disable=not-an-iterable,no-member class TaskList(List[TaskInfo]): @@ -177,9 +202,13 @@ def _set_task_states(self) -> None: """Set the states to task by its dependencies.""" for task in self: if task.status in [TaskState.READY, TaskState.BLOCKED]: - task.status = self.check_dependencies(task.dependencies) - if task.status == TaskState.FAILED: - task.exception = Exception("Failed due to dependency task is failed.") + depend_state = self.check_dependencies(task.dependencies) + if depend_state != TaskState.FAILED: + task.status = depend_state + else: + task.status = TaskState.FAILED if task.inherit_failure else TaskState.READY + if task.status == TaskState.FAILED: + task.exception = Exception("Failed due to dependency task is failed.") def all_finished(self) -> bool: """Get information if all tasks are finished. @@ -192,19 +221,19 @@ def get_task_by_name(self, name: str) -> TaskInfo: """Get the task from this list by its name. :param name: Task name. - :raises ValueError: Task name is not in active task list. + :raises SPSDKError: Task name is not in active task list. :return: Task info object. """ for task in self: if task.name == name: return task - raise ValueError(f"Task {name} not found in list.") + raise SPSDKError(f"Task {name} not found in list.") def check_dependencies(self, dependencies: Optional[List[str]]) -> TaskState: """Checks dependencies if is blocking or not. :param dependencies: List of names of tasks that must be finished before this task. - :raises ValueError: Dependency doesn't exits in task list. + :raises SPSDKError: Dependency doesn't exits in task list. :return: 'Blocks', 'Failed', 'OK' """ if not dependencies: @@ -214,7 +243,7 @@ def check_dependencies(self, dependencies: Optional[List[str]]) -> TaskState: try: task = self.get_task_by_name(depend) except ValueError as exc: - raise ValueError(f"Dependency '{depend}' doesn't exits in task list.") from exc + raise SPSDKError(f"Dependency '{depend}' doesn't exits in task list.") from exc task_dep_st = self.check_dependencies(task.dependencies) if task_dep_st in [TaskState.BLOCKED, TaskState.FAILED]: return task_dep_st @@ -307,9 +336,7 @@ def _print_status(self, repaint: bool = True) -> None: if repaint: self._clear_lines() for task in self.tasks: - self.print_func( - f"\033[K{task.name} -> {task.get_color_by_status()}{task.status_str()}{colorama.Fore.RESET}" - ) + self.print_func(f"\033[K{task.name} -> {task.status_str()}") def _user_task_done_callback( # pylint: disable=no-self-use self, future: Future, future_set: dict, task: TaskInfo diff --git a/tools/tp_setup_workspace.py b/tools/tp_setup_workspace.py index 880b1ccc..d21a8dd8 100644 --- a/tools/tp_setup_workspace.py +++ b/tools/tp_setup_workspace.py @@ -123,7 +123,7 @@ def setup_tp_host_file( tp_target_parameter: dict, family: str, ) -> None: - shutil.copy(f"{TP_DATA_FOLDER}/tphost_cfg_template.yml", "tp_host.yaml") + shutil.copy(f"{TP_DATA_FOLDER}/tphost_cfg_template.yaml", "tp_host.yaml") yaml = ruamel.yaml.YAML() with open("tp_host.yaml") as f: data: dict = yaml.load(f) diff --git a/tox.ini b/tox.ini index 6b4987b5..4eab2c8e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ # content of: tox.ini , put in same dir as setup.py [tox] -envlist = py37,py38,py39,py310,py311 +envlist = py38,py39,py310,py311 [testenv] # install testing framework

=TVBCcCPXUFv$=VwGh)J>Wwj- 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 z z+Dm!*ahOpbj3nG57Tb%_wRVj)X;(=r0Le}#f0z|N8A?POoKti1&)=K- zgJ*_k~`qn8he{DD(PWlHK@7$u`VH2lKLLpE7Y zc|(& zGg{}uY~G&0l=#?$Dss^zYcDAq>e*7X#ngmG0$T(XSkG^P6jD@yaGQE{&>hVJK)Ol| zBSDur--C|FVS~X-67G>hi?0nQjaS4y9MveOW@2YM3DZ1ZS10Yp7PE%O*QI%U`GqLi zM|+wmRLIBDV%qR#P!7cHT+SB_r#zRd%vZF8!dnEPtet_;OF~L;>bnMINh^7)G9WBU~ZFa9A0( zk57Y1T6rf#7mNuZIa1B0pYJ8?dPQa3_GSCkPRV3S%TvWSc|GjBj+Giwq7f44*oz4 zhKJQYnuRzpzj*8JtIj~_j*DL9Y6^f`MY{VLyT3H#9xlcJOXDWN_8{bO+8r8@{D$jg z9CR|shXEw097yU%6jtEZ^r?uB&Ju7kgIi3nk{OF6r9`MX1;)h-ioBJEpac1VLHr%L zVV`uD%uZmkuS&zt{Znr=Gzc&+zbn97J`5VwX+59p>7<11uX3cX=q600rgp*xIH3y! z0i4aB(!T;9uBNLXC@y5TC?q6X1fvmC6FYc8r2)JoKQ&j>e7=d+fxWk~Y!TO0bY%xk zxj7BNKZk|}*@3{ePLcV>=Rp$CXCh)%{sriVqdz#8*rqX?qDVn1!sI!S!+tfN2SG4F z0V!d_OWqFxi?nZ8ub?VT5oy$JvAsQDcCjR`DOt4%<2;!|I7S^G1Z7@T#mfKKe|`V; z+po7!#QqcX-B!e8)AbxmK{TKY1`iWu&)!V|B29w2oCS~$ zyj&y?8w~?oJ3E%qUGu6sa($AJCWVG(D|mC{84`d*(r}i7p}}nuB8drG>){rhGrt8b za=?a3iYQ;J`c@IpBvm!Y3)Ahvq9$-P5rL(LGFxB!a}bS|w23%T9^q33b0t+m;M^di zZlI_2=(_X+ueB#t$*bk@)&ssnHZK_D(k^&KTV@zTrD{TOXNLK>Wo*Cw;~3%nd@Us2 z^W_7-7;vQ?BO_%b#}o{tNVqegcFnf;Gz}ZySA(08d4LB_`{Al~R8;})bE`ODyHLmC zMZhp&A9yE&04*C$K=KSe^6nkvMF@{}tP-tObrj}VS6`>@{P$9FC?9q>5JAlf{V{_s zVQol%#^p8m`JzzepE&pCUm+;QZ(Yrn3wx(tyG7fuwB9PZ8kdMf=jE5D>l5r!s}!?C z?ZYevtLaSSdM+m*PnNv?<8ZIY%9ve*OH*htsckBl-82Z-4z7^+=RzGPelNzDSHD{$RLQJr~p@`3R-}hW?nI z46)__dDXJQsPhEsZW$KcQ$d%k7cLqF4I%Xih8;Wv_2&n*6)fC9Sq$t$p3gDyWp-Pw&6M;p^hNZ%D65S`pz?q!8pJ zu#n29!-Y@-YEp-EBg4BYHb45bN}P|l`FcY3$Mu{BREOpCZIXhM#^QLvCKZwmKT(MS zt=aC1ZW<6dGpGTF8#@oWpA>^gPA}G}LJdX%guAAsb;C!BG{Ps>vSe=$%9-+|78?W- z)+uViCEUmkP<15#rVdakwbexRH25#UDrqlXeEIOVKmGdmk3WA!#)}vH?c%>6FP*&A zv^%9@@8bWvc%fB_z@LI*5cg9aNRU!cB;mc@|0P`lT7ncj70(AQwjd??X^Gk_-3ZQ( z;n2_z(g}sl?UA{J%5e*G7q(UL(D8L*%13qaHuv^`d+0ZjyoBH#Pd81UpinSM#5mGy zl3dGq)-G8_Kw+wJS#*zu@q|tF>F%-4K&ezm&>UNuh6VT4P^8uK>X!7l_J$L}S50|y z-gM}Qy~LhlGNnx^0u~lG>kLcV)jBMuTTsCvY%_Ip#84E8$<}W{mgS5PCX+p#RwO>4 zt;7C7aemo~H2;noS-Mq$-g~U#%t|PaPh}5b1!pSiu9TL7BfPTNj|a*iN)rNF4Ub5N zHMVKpr4ge_gp1R=7tJlPq@ zaAX%qZ0jJ(Qd!_i#(=O15T)pIjPFVrP$;a~>Q|e|4EFU_wP}f<)_BB%tB2VhZ2;bz zI@v_yB~==z=tT!rXXx_v?y9Wkid%vxSqg&94%XT}bG z0&oCwZ<$TeXi#{GN-M5&eS~$Lt*WaEup(c?fEC6U0TS&5_gF_1-Q&_giZ;%)w^?`N z$WQlB+q6vh*QD35z|VmnvpT_ESAE|2tU&6& zoNbrL7&`@aHgY$!%&>W207}rM@|RagyiG8f$w31{Y<_PbaZOs(1tMP{ISWrk(jJ94 zXMEmJ1k&v85#}sQ$iMNA3!vGkCBV{FK3%M+mT)41AN8Nak^&l(x{J(zu2!~396&WF-wYV9u1UJ8@1P@71 z30{$&5=5tIl@Qxf2x#$RkkDrTSZ`oZv6^5q%sbzeQ>+_rT5oj_>p^-eI5h z1em!LPL-tC+C9k=%+r)iZ$V1&qgdjQMZVfzN+cz!VQb!NXRTwq0L#u|t^y{1@Gx6! zr^sW&>o!MjSiFQ6S4XJ(n8gT`FvxIdrhGAcTV%1bn zof?n^6rE*jxr+eZb2Syv!VuyONA#Hb_=z5BW*mkqIMyHclf!npru8aB4HCdvni51rD-iv@}y%F8X`dpDnwc0=e4#v&5PQdUN~93bQhcvZ&~EaJu_ zMWkUH5t;ci#WoCv&EP+F%ZIG?6g{`f=9JeY0nWDun)w@;q&u!tlVuMw1FCJaFhbO8 zxIi>=0yrEDHcFKxMFXOJ0*FlLFU4%AIBE|O0QfELC?M0nuV!DWH7$n!wC-_kkdBhS3_ z-XhiA zNAq-6f`vqg5-cq@VmxcA&pha*ZM<^D9TK^Cn><)CGUb}~d|LgM7m zj5d(W(P)c5UtQ%u_7*ii-1!uXbGp3WGZ?4c05S5oGajw*(GaZD772f1NJg*gUl+ z)Fk-_pOK0*1bq_HLXZk&4GYSQ?7Td=ugNj~qZa?2o#+%rMqp=3u2pwkJy%&8qhf>` z3A{kilibQ?AZ7~<=m*|_907eBmM~pe-s5q}yCs~P(jsE3K|EIqS>oZ`OsisEHJX&m zO{W>1*;#H7mr2b5UOvSb@Xho`Ahe+zQ=Zj=+m?#@$ocz1&VfKiuMe{sfAe1b9gEu` zeAhkTn`PydmpL7wjxI`I4A#`0*NYCO`VkjYTjNQkSWNSerPO1Dh$jQUAu){;>S0mF zmx=cy7r`A$jG2G63!B4L==ACH;FcW;U~*gZf{%r45t18<8y{Fio27`CUg#hXul_Tf z(A)h4z}5_FM^AAfT@aoa;Po9;FY6iq@smM}Cj@i>-mKx!@>IhmHzx2R+Jo>*B$I{D zf}T!n4rR&^)CrB^V=Ac^E^zevlZ2}pP(4jjZLa29u?)YEjg=q4lkQ-OB3DksD`DfO zv#EN*Q7o{aCT>9;bLxy_ri#2a5QM>qEhnAP=-_hKEP6V03}9$CZ}mN{xgY(l!wwEq z(p%ACAt{!T%p+t+UpS7$s6a2b&7wOo1(!jfc%_b~dgfNYIamLzvy-#JfS1^K#>!N& zbj=vuEyHL%6?9zZ4wMYy(?FnTx?M*Iq_q->@KQ*GQAl=UGy;bS!UZ)`@(ytvYu7T= zM`KlkRds#jJ6&8*mDsvy5XLLNXW)_DAQz%91b<=~`7eJV66jtDuQD2&*g~(A><)}c zA$EjeN-dCxwY5chdjM2UF-`4;meg(Jroy~uDNH)uDlS!6{wVYM~2b=j``i|THsM}dW5&W<9d zMBj^&TAgiyBf0JTAYUin=C77Pdw zD5hqk$`+yfMuD9gC^Uto0cWzCZ;@!H3wk(*pi9j`=HbC@$nm-LQJ6fC8p3Jrk>wm6 zBa*d7pR?b6fB5eEL&$`v+~LfqQ!{3>O&F|IWTP;^m*mEJ zfJ-(cUBb{(8Uw;R8a$3V4IY)Z$5kaviP3HgQ6ouSA!#k?D?_H|r!er-QCf5*8r$sm zNtKYEj>O$QGNci?v>KrFwGF`Ot)f73#6ihO%GZGnr(Zt)`n8B)1@uGzVdUzm*$$zWE%sGz z8J68sAuklRFOZrX|@_X)Fp@`G>Ox=dIXbccL6U;?Q1&Tx5l#Tb+pS16SX|eSoT4PZMgYBCxp0Wf)I3krY!8!q8 zq&~DMIhU}4H_X9|=qccl8%`mUTqLGO=5vPRmG{&6==3CL25#ii!t0dWF zi?D@_0zD**)JucD#oZ=G-S0hsv3}^g_^dsPHTYtMgeKXziKG$X42lUG*$9`S9`g06 zHLQM^g-|^HS!WRrQYN1%m5~rN0crSAV6Hu}FlT$QFkit;K*dI)F@&IiC-)L!8;L2!O4oPuzCqY1Xvixu?d zsF+9ZQ4{aU*eHzNF3a0JWhRpz+c$@n%S-K1vUr3Z5b~AC;uuD$243g-2-7}WRab!{ zLLNvBr-&($5*qn>R>(s*=UZpTF*?EsF`-qN8kIl!If*Bmhc78&=2cW!qe)mzv!s|j zF5FMO$-hRt`mo6s38`TQ5uYm$$`>VP^K;Ofi(k`zo1Dcgrxpo(ej}Xdy%m z^`h!;a7EsNS7OmUcQT>f5qxZcK#V%ng9I7i=eUCZKtPOq$w2(8AV() zcXCc$P6}VeGQWsG=t!g??=~ZhLiKd{n`s==KX^HvM^6Oo{Qh`YFITXaTP3-Vc=Vt< za)0dS_0MRQ@KV;WO(ol%pHYh81dFS=3~VEH0kS+=e5%1=GdZ2}9*M3HSyg?s!lCwY zmy?-+kuPBwLBxsiZy*$y(r%GF4hY2;6nsJM9x3HG-Sb}Zrtn%dzD0V7&zJ|<;m={4 z#iZABs*m_UNnllRwW+fv>unsM&|gQ7yC1Y(AIchZaJG{$q4RZhVvAos{3bE(cF^Uy z{`U1E=`vC<0Nh2Ou8~CFs^!ai8;6PZ*U<^;1&K~ym5N2)tD;#Lw4+lyt6E~Axb-q0 zK*}8Nmrx`ZZ}b^jp|^|HH2P~Q1foR@1W-tmqxZj=kulS9Fs9n9PgRIj&FQ z?7U~ZCGCfcs}r#W?8jzA$ƙs_Xdxnxh>xUI7cDK(NnwiUUtN4)N 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