diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index f5ca11da190..0169a837683 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1300,12 +1300,26 @@ def check_no_undef_outside_kconfig(self, kconf): "FOO_LOG_LEVEL", "FOO_SETTING_1", "FOO_SETTING_2", - "GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_APPROTECT_APPLICATION_PROTECTED", + "GEN_UICR_APPROTECT_CORESIGHT_PROTECTED", + "GEN_UICR_APPROTECT_RADIOCORE_PROTECTED", + "GEN_UICR_ERASEPROTECT", + "GEN_UICR_GENERATE_PERIPHCONF", + "GEN_UICR_LOCK", "GEN_UICR_PROTECTEDMEM", "GEN_UICR_PROTECTEDMEM_SIZE_BYTES", "GEN_UICR_SECONDARY", "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", "GEN_UICR_SECONDARY_PROCESSOR_VALUE", + "GEN_UICR_SECONDARY_PROTECTEDMEM", + "GEN_UICR_SECONDARY_PROTECTEDMEM_SIZE_BYTES", + "GEN_UICR_SECONDARY_TRIGGER", + "GEN_UICR_SECONDARY_TRIGGER_APPLICATIONLOCKUP", + "GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT0", + "GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT1", + "GEN_UICR_SECONDARY_TRIGGER_RADIOCORELOCKUP", + "GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT0", + "GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT1", "GEN_UICR_SECONDARY_WDTSTART", "GEN_UICR_SECONDARY_WDTSTART_CRV", "GEN_UICR_SECONDARY_WDTSTART_INSTANCE_CODE", diff --git a/soc/nordic/common/uicr/gen_uicr.py b/soc/nordic/common/uicr/gen_uicr.py index da2938bf05c..8dbbc7fe5ce 100644 --- a/soc/nordic/common/uicr/gen_uicr.py +++ b/soc/nordic/common/uicr/gen_uicr.py @@ -25,6 +25,8 @@ # Common values for representing enabled/disabled in the UICR format. ENABLED_VALUE = 0xFFFF_FFFF DISABLED_VALUE = 0xBD23_28A8 +PROTECTED_VALUE = ENABLED_VALUE # UICR_PROTECTED = UICR_ENABLED per uicr_defs.h +UNPROTECTED_VALUE = DISABLED_VALUE # Unprotected uses the default erased value KB_4 = 4096 @@ -430,6 +432,39 @@ def main() -> None: type=lambda s: int(s, 0), help="Size in bytes of cpurad_its_partition (decimal or 0x-prefixed hex)", ) + parser.add_argument( + "--permit-permanently-transitioning-device-to-deployed", + action="store_true", + help=( + "Safety flag required to enable both UICR.LOCK and UICR.ERASEPROTECT together. " + "Must be explicitly provided to acknowledge permanent device state changes." + ), + ) + parser.add_argument( + "--lock", + action="store_true", + help="Enable UICR.LOCK to prevent modifications without ERASEALL", + ) + parser.add_argument( + "--eraseprotect", + action="store_true", + help="Enable UICR.ERASEPROTECT to block ERASEALL operations", + ) + parser.add_argument( + "--approtect-application-protected", + action="store_true", + help="Protect application domain access port (disable debug access)", + ) + parser.add_argument( + "--approtect-radiocore-protected", + action="store_true", + help="Protect radio core access port (disable debug access)", + ) + parser.add_argument( + "--approtect-coresight-protected", + action="store_true", + help="Protect CoreSight access port (disable debug access)", + ) parser.add_argument( "--protectedmem", action="store_true", @@ -487,6 +522,26 @@ def main() -> None: type=lambda s: int(s, 0), help="Processor to boot for the secondary firmware ", ) + parser.add_argument( + "--secondary-trigger", + action="store_true", + help="Enable UICR.SECONDARY.TRIGGER for automatic secondary firmware boot on reset events", + ) + parser.add_argument( + "--secondary-trigger-resetreas", + default=0, + type=lambda s: int(s, 0), + help=( + "Bitmask of reset reasons that trigger secondary firmware boot " + "(decimal or 0x-prefixed hex)" + ), + ) + parser.add_argument( + "--secondary-protectedmem-size", + default=None, + type=lambda s: int(s, 0), + help="Size in bytes of the secondary protected memory region (decimal or 0x-prefixed hex)", + ) parser.add_argument( "--secondary-periphconf-address", default=None, @@ -577,6 +632,33 @@ def main() -> None: uicr.SECURESTORAGE.ITS.APPLICATIONSIZE1KB = args.cpuapp_its_size // 1024 uicr.SECURESTORAGE.ITS.RADIOCORESIZE1KB = args.cpurad_its_size // 1024 + # Handle LOCK and ERASEPROTECT configuration + # Check if both are enabled together - this requires explicit acknowledgment + if ( + args.lock + and args.eraseprotect + and not args.permit_permanently_transitioning_device_to_deployed + ): + raise ScriptError( + "Enabling both --lock and --eraseprotect requires " + "--permit-permanently-transitioning-device-to-deployed to be specified. " + "This combination permanently locks the device configuration and prevents " + "ERASEALL." + ) + + if args.lock: + uicr.LOCK = ENABLED_VALUE + if args.eraseprotect: + uicr.ERASEPROTECT = ENABLED_VALUE + # Handle APPROTECT configuration + if args.approtect_application_protected: + uicr.APPROTECT.APPLICATION = PROTECTED_VALUE + + if args.approtect_radiocore_protected: + uicr.APPROTECT.RADIOCORE = PROTECTED_VALUE + + if args.approtect_coresight_protected: + uicr.APPROTECT.CORESIGHT = PROTECTED_VALUE # Handle protected memory configuration if args.protectedmem: if args.protectedmem_size_bytes % KB_4 != 0: @@ -628,6 +710,20 @@ def main() -> None: uicr.SECONDARY.ADDRESS = args.secondary_address uicr.SECONDARY.PROCESSOR = args.secondary_processor + # Handle secondary TRIGGER configuration + if args.secondary_trigger: + uicr.SECONDARY.TRIGGER.ENABLE = ENABLED_VALUE + uicr.SECONDARY.TRIGGER.RESETREAS = args.secondary_trigger_resetreas + + # Handle secondary PROTECTEDMEM configuration + if args.secondary_protectedmem_size: + uicr.SECONDARY.PROTECTEDMEM.ENABLE = ENABLED_VALUE + if args.secondary_protectedmem_size % 4096 != 0: + raise ScriptError( + f"args.secondary_protectedmem_size was {args.secondary_protectedmem_size}, " + f"but must be divisible by 4096" + ) + uicr.SECONDARY.PROTECTEDMEM.SIZE4KB = args.secondary_protectedmem_size // 4096 # Handle secondary periphconf if provided if args.out_secondary_periphconf_hex: secondary_periphconf_combined = extract_and_combine_periphconfs( diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index 39737888023..290cf16a0a6 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -75,6 +75,9 @@ endfunction() if(CMAKE_VERBOSE_MAKEFILE) endif() +set(lock_args) +set(eraseprotect_args) +set(approtect_args) set(protectedmem_args) set(periphconf_args) set(wdtstart_args) @@ -115,6 +118,29 @@ if(CONFIG_GEN_UICR_SECURESTORAGE) list(APPEND securestorage_args --cpurad-its-size ${CPURAD_ITS_SIZE}) endif(CONFIG_GEN_UICR_SECURESTORAGE) +# Handle LOCK configuration +if(CONFIG_GEN_UICR_LOCK) + list(APPEND lock_args --lock) +endif() + +# Handle ERASEPROTECT configuration +if(CONFIG_GEN_UICR_ERASEPROTECT) + list(APPEND eraseprotect_args --eraseprotect) +endif() + +# Handle APPROTECT configuration +if(CONFIG_GEN_UICR_APPROTECT_APPLICATION_PROTECTED) + list(APPEND approtect_args --approtect-application-protected) +endif() + +if(CONFIG_GEN_UICR_APPROTECT_RADIOCORE_PROTECTED) + list(APPEND approtect_args --approtect-radiocore-protected) +endif() + +if(CONFIG_GEN_UICR_APPROTECT_CORESIGHT_PROTECTED) + list(APPEND approtect_args --approtect-coresight-protected) +endif() + # Handle protected memory configuration if(CONFIG_GEN_UICR_PROTECTEDMEM) list(APPEND protectedmem_args --protectedmem) @@ -188,6 +214,39 @@ if(CONFIG_GEN_UICR_SECONDARY) list(APPEND secondary_args --secondary-wdtstart-crv ${CONFIG_GEN_UICR_SECONDARY_WDTSTART_CRV}) endif() + # Handle secondary TRIGGER configuration + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER) + list(APPEND secondary_args --secondary-trigger) + + # Compute RESETREAS bitmask from individual trigger configs + set(resetreas_value 0) + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT0) + math(EXPR resetreas_value "${resetreas_value} + 0x001") + endif() + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT1) + math(EXPR resetreas_value "${resetreas_value} + 0x002") + endif() + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_APPLICATIONLOCKUP) + math(EXPR resetreas_value "${resetreas_value} + 0x008") + endif() + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT0) + math(EXPR resetreas_value "${resetreas_value} + 0x020") + endif() + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT1) + math(EXPR resetreas_value "${resetreas_value} + 0x040") + endif() + if(CONFIG_GEN_UICR_SECONDARY_TRIGGER_RADIOCORELOCKUP) + math(EXPR resetreas_value "${resetreas_value} + 0x100") + endif() + + list(APPEND secondary_args --secondary-trigger-resetreas ${resetreas_value}) + endif() + + # Handle secondary PROTECTEDMEM configuration + if(CONFIG_GEN_UICR_SECONDARY_PROTECTEDMEM) + list(APPEND secondary_args --secondary-protectedmem-size ${CONFIG_GEN_UICR_SECONDARY_PROTECTEDMEM_SIZE_BYTES}) + endif() + if(CONFIG_GEN_UICR_SECONDARY_GENERATE_PERIPHCONF) # Compute SECONDARY_PERIPHCONF absolute address and size from this image's devicetree compute_partition_address_and_size("secondary_periphconf_partition" SECONDARY_PERIPHCONF_ADDRESS SECONDARY_PERIPHCONF_SIZE) @@ -210,6 +269,9 @@ add_custom_command( --uicr-address ${UICR_ADDRESS} --out-merged-hex ${merged_hex_file} --out-uicr-hex ${uicr_hex_file} + ${lock_args} + ${eraseprotect_args} + ${approtect_args} ${wdtstart_args} ${periphconf_args} ${securestorage_args} diff --git a/soc/nordic/common/uicr/gen_uicr/Kconfig b/soc/nordic/common/uicr/gen_uicr/Kconfig index 9b630139e64..3322d5cb9cb 100644 --- a/soc/nordic/common/uicr/gen_uicr/Kconfig +++ b/soc/nordic/common/uicr/gen_uicr/Kconfig @@ -32,6 +32,53 @@ config GEN_UICR_SECURESTORAGE - At least one subpartition must be defined - Combined subpartition sizes must equal secure_storage_partition size +config GEN_UICR_LOCK + bool "Enable UICR.LOCK" + help + When enabled, locks the entire contents of the NVR0 page located in + MRAM10. This includes all values in both the UICR and the BICR (Board + Information Configuration Registers). Once locked, the UICR can only + be modified by performing an ERASEALL operation. + + This should be enabled only in production devices to prevent + unauthorized modification. + +config GEN_UICR_ERASEPROTECT + bool "Enable UICR.ERASEPROTECT" + depends on ! GEN_UICR_LOCK + help + When enabled, ERASEALL operations are blocked. + + This option is mutually exclusive with UICR.LOCK in Kconfig to prevent + accidental configuration where both are enabled simultaneously. If both + were enabled, the UICR would become impossible to modify in any way. + Note that gen_uicr.py can be used directly to create a configuration + with both enabled if needed. + +menu "UICR.APPROTECT - Access Port Protection" + +config GEN_UICR_APPROTECT_APPLICATION_PROTECTED + bool "Protect application domain access port" + help + When enabled, disables debug access to the application domain processor, + preventing debugger connection to application memory, registers, and debug + features. When disabled, full debug access is enabled. + +config GEN_UICR_APPROTECT_RADIOCORE_PROTECTED + bool "Protect radio core access port" + help + When enabled, disables debug access to the radio core processor, + preventing debugger connection to radio core memory, registers, and debug + features. When disabled, full debug access is enabled. + +config GEN_UICR_APPROTECT_CORESIGHT_PROTECTED + bool "Disable CoreSight subsystem" + help + When enabled will disable the coresight subsystem, preventing + system level trace features. + +endmenu + config GEN_UICR_PROTECTEDMEM bool "Enable UICR.PROTECTEDMEM" help @@ -166,6 +213,62 @@ config GEN_UICR_SECONDARY_PROCESSOR_VALUE default 0xBD2328A8 if GEN_UICR_SECONDARY_PROCESSOR_APPLICATION default 0x1730C77F if GEN_UICR_SECONDARY_PROCESSOR_RADIOCORE +config GEN_UICR_SECONDARY_TRIGGER + bool "Enable UICR.SECONDARY.TRIGGER" + help + When enabled, configures automatic triggers that cause IronSide SE + to boot the secondary firmware instead of the primary firmware based + on specific reset reasons. + +if GEN_UICR_SECONDARY_TRIGGER + +config GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT0 + bool "Trigger on Application domain watchdog 0 reset" + help + Boot secondary firmware when Application domain watchdog 0 causes a reset. + +config GEN_UICR_SECONDARY_TRIGGER_APPLICATIONWDT1 + bool "Trigger on Application domain watchdog 1 reset" + help + Boot secondary firmware when Application domain watchdog 1 causes a reset. + +config GEN_UICR_SECONDARY_TRIGGER_APPLICATIONLOCKUP + bool "Trigger on Application domain CPU lockup reset" + help + Boot secondary firmware when Application domain CPU lockup causes a reset. + +config GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT0 + bool "Trigger on Radio core watchdog 0 reset" + help + Boot secondary firmware when Radio core watchdog 0 causes a reset. + +config GEN_UICR_SECONDARY_TRIGGER_RADIOCOREWDT1 + bool "Trigger on Radio core watchdog 1 reset" + help + Boot secondary firmware when Radio core watchdog 1 causes a reset. + +config GEN_UICR_SECONDARY_TRIGGER_RADIOCORELOCKUP + bool "Trigger on Radio core CPU lockup reset" + help + Boot secondary firmware when Radio core CPU lockup causes a reset. + +endif # GEN_UICR_SECONDARY_TRIGGER + +config GEN_UICR_SECONDARY_PROTECTEDMEM + bool "Enable UICR.SECONDARY.PROTECTEDMEM" + depends on GEN_UICR_SECONDARY + help + When enabled, the UICR generator will configure the + protected memory region for the secondary firmware. + +config GEN_UICR_SECONDARY_PROTECTEDMEM_SIZE_BYTES + int "Secondary protected memory size in bytes" + default 4096 + depends on GEN_UICR_SECONDARY_PROTECTEDMEM + help + Size of the secondary protected memory region in bytes. + This value must be divisible by 4096 (4 kiB). + endif # GEN_UICR_SECONDARY endmenu