diff --git a/cmake/sysbuild/suit.cmake b/cmake/sysbuild/suit.cmake index d7c2e92fbb38..d5d8dab45f44 100644 --- a/cmake/sysbuild/suit.cmake +++ b/cmake/sysbuild/suit.cmake @@ -521,6 +521,7 @@ function(suit_create_package) "${DEFAULT_BINARY_DIR}/zephyr" --zephyr-base ${ZEPHYR_BASE} --config-file "${PROJECT_BINARY_DIR}/.config" + --soc ${SB_CONFIG_SOC} ${CORE_ARGS} ) set_property( diff --git a/config/suit/templates/nrf9280/default/v1/app_envelope.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/app_envelope.yaml.jinja2 new file mode 100644 index 000000000000..3fa86952d928 --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/app_envelope.yaml.jinja2 @@ -0,0 +1,177 @@ +{%- set mpi_application_vendor_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_1_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_application_class_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_1_CLASS_NAME']|default('nRF9280_sample_app') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if APP_LOCAL_1_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ APP_LOCAL_1_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + - - MEM + - {{ application['dt'].label2node['cpu'].unit_addr }} + - {{ get_absolute_address(application['dt'].chosen_nodes['zephyr,code-partition']) }} + - {{ application['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }} + - - CAND_IMG + - 0 + suit-shared-sequence: + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_application_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_application_vendor_name }} + name: {{ mpi_application_class_name }} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ application['binary'] }} + suit-parameter-image-size: + file: {{ application['binary'] }} + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-validate: + - suit-directive-set-component-index: 0 + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-invoke: + - suit-directive-set-component-index: 0 + - suit-directive-invoke: + - suit-send-record-failure + +{%- if APP_LOCAL_1_VERSION is defined %} + suit-current-version: {{ APP_LOCAL_1_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if application['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Application DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in application['config'] and application['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ application['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ application['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ application['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-source-component: 1 + # When copying the data it is worth to retry the sequence of + # suit-directive-copy and suit-condition-image-match at least once. + # If a bit flip occurs, it might be due to a transport issue, not + # a corrupted candidate image. In this case the bit flip is recoverable + # and it is worth retrying the operation. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-text: + suit-digest-algorithm-id: cose-alg-sha-256 + + suit-candidate-verification: + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in application['config'] and application['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ application['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ application['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ application['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_application_vendor_name }} + name: {{ mpi_application_class_name }} + suit-text: + en: + '["MEM", {{ application['dt'].label2node['cpu'].unit_addr }}, {{ get_absolute_address(application['dt'].chosen_nodes['zephyr,code-partition']) }}, {{ application['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }}]': + suit-text-vendor-name: Nordic Semiconductor ASA + suit-text-model-name: nRF9280_cpuapp + suit-text-vendor-domain: nordicsemi.com + suit-text-model-info: The nRF9280 application core + suit-text-component-description: Sample application core FW + suit-text-component-version: v1.0.0 +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE' not in application['config'] or application['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE'] == '' %} + suit-integrated-payloads: + '#{{ application['name'] }}': {{ application['binary'] }} +{%- endif %} diff --git a/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_app_local.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_app_local.yaml.jinja2 new file mode 100644 index 000000000000..16247d565656 --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_app_local.yaml.jinja2 @@ -0,0 +1,194 @@ +{%- set component_index = 0 %} +{%- set component_list = [] %} +{%- set dependencies_list = [] %} +{%- set mpi_app_recovery_vendor_name = application['config']['CONFIG_SUIT_MPI_APP_RECOVERY_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_app_recovery_class_name = application['config']['CONFIG_SUIT_MPI_APP_RECOVERY_CLASS_NAME']|default('nRF9280_app_recovery') %} +{%- set mpi_rad_recovery_vendor_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_rad_recovery_class_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_CLASS_NAME']|default('nRF9280_rad_recovery') %} +{%- set mpi_app_recovery_local_vendor_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_3_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_app_recovery_local_class_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_3_CLASS_NAME']|default('nRF9280_sample_app_2') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if APP_RECOVERY_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ APP_RECOVERY_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + {%- set component_index = 0 %} + - - CAND_MFST + - 0 + {%- set component_index = component_index + 1 %} +{%- if app_recovery_img is defined %} + {%- set app_recovery_local_instld_mfst_component_index = component_index %} + {{- component_list.append( app_recovery_local_instld_mfst_component_index ) or ""}} + {{- dependencies_list.append( app_recovery_local_instld_mfst_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_app_recovery_local_vendor_name }} + name: {{ mpi_app_recovery_local_class_name }} + {%- set component_index = component_index + 1 %} +{%- endif %} +{%- if rad_recovery is defined %} + {%- set rad_instld_mfst_component_index = component_index %} + {{- component_list.append( rad_instld_mfst_component_index ) or ""}} + {{- dependencies_list.append( rad_instld_mfst_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_rad_recovery_vendor_name }} + name: {{ mpi_rad_recovery_class_name }} + {%- set component_index = component_index + 1 %} +{%- endif %} + + suit-shared-sequence: +{%- if rad_recovery is defined %} + - suit-directive-set-component-index: {{ rad_instld_mfst_component_index }} + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_app_recovery_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_app_recovery_vendor_name }} + name: {{ mpi_app_recovery_class_name }} +{%- endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: {{ app_recovery_local_instld_mfst_component_index }} + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_app_recovery_local_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_app_recovery_local_vendor_name }} + name: {{ mpi_app_recovery_class_name }} +{%- endif %} + - suit-directive-set-component-index: [{{ component_list|join(',') }}] + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-dependencies: + # Key is the index of suit-components that describe the dependency manifest + "0": {} +{%- for component_element in dependencies_list %} + "{{ component_element }}": {} +{%- endfor %} + + suit-validate: +{% if dependencies_list|length > 0 %} + - suit-directive-set-component-index: [{{ dependencies_list|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{% endif %} + + suit-invoke: +{% if dependencies_list|length > 0 %} + - suit-directive-set-component-index: [{{ dependencies_list|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{% endif %} + +{%- if APP_RECOVERY_VERSION is defined %} + suit-current-version: {{ APP_RECOVERY_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if application['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Application DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} +{%- if rad_recovery is defined %} + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ rad_recovery['name'] }}' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ artifacts_folder ~ rad_recovery['name'] }}.suit + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ app_recovery_img['name'] }}' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ artifacts_folder ~ app_recovery_img['name'] }}.suit + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_app_recovery_vendor_name }} + name: {{ mpi_app_recovery_class_name }} + suit-integrated-dependencies: +{%- if rad_recovery is defined %} + '#{{ rad_recovery['name'] }}': {{ artifacts_folder ~ rad_recovery['name'] }}.suit +{%- endif %} +{%- if app_recovery_img is defined %} + '#{{ app_recovery_img['name'] }}': {{ artifacts_folder ~ app_recovery_img['name'] }}.suit +{%- endif %} \ No newline at end of file diff --git a/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_direct.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_direct.yaml.jinja2 new file mode 100644 index 000000000000..64d357606d3a --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/app_recovery_envelope_direct.yaml.jinja2 @@ -0,0 +1,219 @@ +{%- set component_index = 0 %} +{%- set component_list = [] %} +{%- set dependencies_list = [] %} +{%- set mpi_app_recovery_vendor_name = application['config']['CONFIG_SUIT_MPI_APP_RECOVERY_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_app_recovery_class_name = application['config']['CONFIG_SUIT_MPI_APP_RECOVERY_CLASS_NAME']|default('nRF9280_app_recovery') %} +{%- set mpi_rad_recovery_vendor_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_rad_recovery_class_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_CLASS_NAME']|default('nRF9280_rad_recovery') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if APP_RECOVERY_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ APP_RECOVERY_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + {%- set component_index = 0 %} + - - CAND_IMG + - 0 + {%- set component_index = component_index + 1 %} + - - CAND_MFST + - 0 + {%- set component_index = component_index + 1 %} +{%- if app_recovery_img is defined %} + {%- set app_img_component_index = component_index %} + {{- component_list.append( app_img_component_index ) or ""}} + - - MEM + - {{ app_recovery_img['dt'].label2node['cpu'].unit_addr }} + - {{ get_absolute_address(app_recovery_img['dt'].chosen_nodes['zephyr,code-partition']) }} + - {{ app_recovery_img['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }} + {%- set component_index = component_index + 1 %} +{%- endif %} +{%- if rad_recovery is defined %} + {%- set rad_instld_mfst_component_index = component_index %} + {{- component_list.append( rad_instld_mfst_component_index ) or ""}} + {{- dependencies_list.append( rad_instld_mfst_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_rad_recovery_vendor_name }} + name: {{ mpi_rad_recovery_class_name }} + {%- set component_index = component_index + 1 %} +{%- endif %} + + suit-shared-sequence: +{%- if rad_recovery is defined %} + - suit-directive-set-component-index: {{ rad_instld_mfst_component_index }} + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_app_recovery_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_app_recovery_vendor_name }} + name: {{ mpi_app_recovery_class_name }} +{%- endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: {{ app_img_component_index }} + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_app_recovery_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_app_recovery_vendor_name }} + name: {{ mpi_app_recovery_class_name }} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ app_recovery_img['binary'] }} + suit-parameter-image-size: + file: {{ app_recovery_img['binary'] }} +{%- endif %} + - suit-directive-set-component-index: [{{ component_list|join(',') }}] + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-dependencies: + # Key is the index of suit-components that describe the dependency manifest + "1": {} +{%- for component_element in dependencies_list %} + "{{ component_element }}": {} +{%- endfor %} + + suit-validate: +{% if dependencies_list|length > 0 %} + - suit-directive-set-component-index: [{{ dependencies_list|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{% endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: {{ app_img_component_index }} + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} + + suit-invoke: +{% if dependencies_list|length > 0 %} + - suit-directive-set-component-index: [{{ dependencies_list|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{% endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: {{ app_img_component_index }} + - suit-directive-invoke: + - suit-send-record-failure +{%- endif %} + +{%- if APP_RECOVERY_VERSION is defined %} + suit-current-version: {{ APP_RECOVERY_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if application['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Application DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} +{%- if rad_recovery is defined %} + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ rad_recovery['name'] }}' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ artifacts_folder ~ rad_recovery['name'] }}.suit + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if app_recovery_img is defined %} + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in app_recovery_img['config'] and app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ app_recovery_img['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ app_recovery_img['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: {{ app_img_component_index }} + - suit-directive-override-parameters: + suit-parameter-source-component: 0 + - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_app_recovery_vendor_name }} + name: {{ mpi_app_recovery_class_name }} + suit-integrated-dependencies: +{%- if rad_recovery is defined %} + '#{{ rad_recovery['name'] }}': {{ artifacts_folder ~ rad_recovery['name'] }}.suit +{%- endif %} +{%- if app_recovery_img is defined %} +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE' not in app_recovery_img['config'] or app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE'] == '' %} + '#{{ app_recovery_img['name'] }}': {{ app_recovery_img['binary'] }} +{%- endif %} +{%- endif %} \ No newline at end of file diff --git a/config/suit/templates/nrf9280/default/v1/app_recovery_local_envelope.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/app_recovery_local_envelope.yaml.jinja2 new file mode 100644 index 000000000000..c8e8aa6fb7ee --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/app_recovery_local_envelope.yaml.jinja2 @@ -0,0 +1,177 @@ +{%- set mpi_app_recovery_local_vendor_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_3_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_app_recovery_local_class_name = application['config']['CONFIG_SUIT_MPI_APP_LOCAL_3_CLASS_NAME']|default('nRF9280_sample_app_3') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if APP_LOCAL_3_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ APP_LOCAL_3_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + - - MEM + - {{ app_recovery_img['dt'].label2node['cpu'].unit_addr }} + - {{ get_absolute_address(app_recovery_img['dt'].chosen_nodes['zephyr,code-partition']) }} + - {{ app_recovery_img['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }} + - - CAND_IMG + - 0 + suit-shared-sequence: + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_app_recovery_local_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_app_recovery_local_vendor_name }} + name: {{ mpi_app_recovery_local_class_name }} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ app_recovery_img['binary'] }} + suit-parameter-image-size: + file: {{ app_recovery_img['binary'] }} + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-validate: + - suit-directive-set-component-index: 0 + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-invoke: + - suit-directive-set-component-index: 0 + - suit-directive-invoke: + - suit-send-record-failure + +{%- if APP_LOCAL_3_VERSION is defined %} + suit-current-version: {{ APP_LOCAL_3_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if application['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Application DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in app_recovery_img['config'] and app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ app_recovery_img['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ app_recovery_img['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-source-component: 1 + # When copying the data it is worth to retry the sequence of + # suit-directive-copy and suit-condition-image-match at least once. + # If a bit flip occurs, it might be due to a transport issue, not + # a corrupted candidate image. In this case the bit flip is recoverable + # and it is worth retrying the operation. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-text: + suit-digest-algorithm-id: cose-alg-sha-256 + + suit-candidate-verification: + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in app_recovery_img['config'] and app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ app_recovery_img['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ app_recovery_img['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_app_recovery_local_vendor_name }} + name: {{ mpi_app_recovery_local_class_name }} + suit-text: + en: + '["MEM", {{ app_recovery_img['dt'].label2node['cpu'].unit_addr }}, {{ get_absolute_address(app_recovery_img['dt'].chosen_nodes['zephyr,code-partition']) }}, {{ app_recovery_img['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }}]': + suit-text-vendor-name: Nordic Semiconductor ASA + suit-text-model-name: nRF9280_cpuapp_3 + suit-text-vendor-domain: nordicsemi.com + suit-text-model-info: The nRF9280 application core companion/recovery + suit-text-component-description: Sample application core companion/recovery FW + suit-text-component-version: v1.0.0 +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE' not in app_recovery_img['config'] or app_recovery_img['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE'] == '' %} + suit-integrated-payloads: + '#{{ app_recovery_img['name'] }}': {{ app_recovery_img['binary'] }} +{%- endif %} diff --git a/config/suit/templates/nrf9280/default/v1/rad_envelope.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/rad_envelope.yaml.jinja2 new file mode 100644 index 000000000000..e79850cb4a29 --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/rad_envelope.yaml.jinja2 @@ -0,0 +1,183 @@ +{%- if application is defined %} + {%- set main_config = application %} +{%- else %} + {%- set main_config = radio %} +{%- endif %} +{%- set mpi_radio_vendor_name = main_config['config']['CONFIG_SUIT_MPI_RAD_LOCAL_1_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_radio_class_name = main_config['config']['CONFIG_SUIT_MPI_RAD_LOCAL_1_CLASS_NAME']|default('nRF9280_sample_rad') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if RAD_LOCAL_1_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ RAD_LOCAL_1_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + - - MEM + - {{ radio['dt'].label2node['cpu'].unit_addr }} + - {{ get_absolute_address(radio['dt'].chosen_nodes['zephyr,code-partition']) }} + - {{ radio['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }} + - - CAND_IMG + - 0 + suit-shared-sequence: + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_radio_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_radio_vendor_name }} + name: {{ mpi_radio_class_name }} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ radio['binary'] }} + suit-parameter-image-size: + file: {{ radio['binary'] }} + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-validate: + - suit-directive-set-component-index: 0 + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-invoke: + - suit-directive-set-component-index: 0 + - suit-directive-invoke: + - suit-send-record-failure + +{%- if RAD_LOCAL_1_VERSION is defined %} + suit-current-version: {{ RAD_LOCAL_1_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if radio['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Radio DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in radio['config'] and radio['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ radio['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ radio['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ radio['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-source-component: 1 + # When copying the data it is worth to retry the sequence of + # suit-directive-copy and suit-condition-image-match at least once. + # If a bit flip occurs, it might be due to a transport issue, not + # a corrupted candidate image. In this case the bit flip is recoverable + # and it is worth retrying the operation. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-text: + suit-digest-algorithm-id: cose-alg-sha-256 + + suit-candidate-verification: + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in radio['config'] and radio['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ radio['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ radio['name'] }}' +{%- endif %} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ radio['binary'] }} + - suit-directive-fetch: + - suit-send-record-failure + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_radio_vendor_name }} + name: {{ mpi_radio_class_name }} + suit-text: + en: + '["MEM", {{ radio['dt'].label2node['cpu'].unit_addr }}, {{ get_absolute_address(radio['dt'].chosen_nodes['zephyr,code-partition']) }}, {{ radio['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }}]': + suit-text-vendor-name: Nordic Semiconductor ASA + suit-text-model-name: nRF9280_cpurad + suit-text-vendor-domain: nordicsemi.com + suit-text-model-info: The nRF9280 radio core + suit-text-component-description: Sample radio core FW + suit-text-component-version: v1.0.0 + +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE' not in radio['config'] or radio['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE'] == '' %} + suit-integrated-payloads: + '#{{ radio['name'] }}': {{ radio['binary'] }} +{%- endif %} diff --git a/config/suit/templates/nrf9280/default/v1/rad_recovery_envelope.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/rad_recovery_envelope.yaml.jinja2 new file mode 100644 index 000000000000..f70d00f77630 --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/rad_recovery_envelope.yaml.jinja2 @@ -0,0 +1,113 @@ +{%- set mpi_rad_recovery_vendor_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_rad_recovery_class_name = application['config']['CONFIG_SUIT_MPI_RAD_RECOVERY_CLASS_NAME']|default('nRF9280_sample_rad_recovery') %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if RAD_RECOVERY_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ RAD_RECOVERY_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + - - MEM + - {{ rad_recovery['dt'].label2node['cpu'].unit_addr }} + - {{ get_absolute_address(rad_recovery['dt'].chosen_nodes['zephyr,code-partition']) }} + - {{ rad_recovery['dt'].chosen_nodes['zephyr,code-partition'].regs[0].size }} + - - CAND_IMG + - 0 + suit-shared-sequence: + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_rad_recovery_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_rad_recovery_vendor_name }} + name: {{ mpi_rad_recovery_class_name }} + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ rad_recovery['binary'] }} + suit-parameter-image-size: + file: {{ rad_recovery['binary'] }} + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + file: {{ rad_recovery['binary'] }} + suit-validate: + - suit-directive-set-component-index: 0 + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-invoke: + - suit-directive-set-component-index: 0 + - suit-directive-invoke: + - suit-send-record-failure + +{%- if RAD_RECOVERY_VERSION is defined %} + suit-current-version: {{ RAD_RECOVERY_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if radio['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Radio DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} + - suit-directive-set-component-index: 1 + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ rad_recovery['name'] }}' +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI' in rad_recovery['config'] and rad_recovery['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] != '' %} + suit-parameter-uri: '{{ rad_recovery['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE_URI'] }}' +{%- else %} + suit-parameter-uri: '#{{ rad_recovery['name'] }}' +{%- endif %} + + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-set-component-index: 0 + - suit-directive-override-parameters: + suit-parameter-source-component: 1 + - suit-directive-copy: + - suit-send-record-failure + - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_rad_recovery_vendor_name }} + name: {{ mpi_rad_recovery_class_name }} +{%- if 'CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE' not in rad_recovery['config'] or rad_recovery['config']['CONFIG_SUIT_DFU_CACHE_EXTRACT_IMAGE'] == '' %} + suit-integrated-payloads: + '#{{ rad_recovery['name'] }}': {{ rad_recovery['binary'] }} +{%- endif %} diff --git a/config/suit/templates/nrf9280/default/v1/root_with_binary_nordic_top.yaml.jinja2 b/config/suit/templates/nrf9280/default/v1/root_with_binary_nordic_top.yaml.jinja2 new file mode 100644 index 000000000000..1d9aa41c4ac5 --- /dev/null +++ b/config/suit/templates/nrf9280/default/v1/root_with_binary_nordic_top.yaml.jinja2 @@ -0,0 +1,301 @@ +{%- set component_index = 0 %} +{%- set component_list = [] %} +{%- if application is not defined %} + {%- set main_config = radio %} +{%- else %} + {%- set main_config = application %} +{%- endif %} +{%- set mpi_root_vendor_name = main_config['config']['CONFIG_SUIT_MPI_ROOT_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_root_class_name = main_config['config']['CONFIG_SUIT_MPI_ROOT_CLASS_NAME']|default('nRF9280_sample_root') %} +{%- set mpi_application_vendor_name = main_config['config']['CONFIG_SUIT_MPI_APP_LOCAL_1_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_application_class_name = main_config['config']['CONFIG_SUIT_MPI_APP_LOCAL_1_CLASS_NAME']|default('nRF9280_sample_app') %} +{%- set mpi_radio_vendor_name = main_config['config']['CONFIG_SUIT_MPI_RAD_LOCAL_1_VENDOR_NAME']|default('nordicsemi.com') %} +{%- set mpi_radio_class_name = main_config['config']['CONFIG_SUIT_MPI_RAD_LOCAL_1_CLASS_NAME']|default('nRF9280_sample_rad') %} +{%- if 'SB_CONFIG_SUIT_ENVELOPE_NORDIC_TOP_DIRECTORY' in sysbuild['config'] and sysbuild['config']['SB_CONFIG_SUIT_ENVELOPE_NORDIC_TOP_DIRECTORY'] != '' %} + {%- set nordic_top = True %} +{%- else %} + {%- set nordic_top = False %} +{%- endif %} +SUIT_Envelope_Tagged: + suit-authentication-wrapper: + SuitDigest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-manifest: + suit-manifest-version: 1 +{%- if APP_ROOT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ APP_ROOT_SEQ_NUM }} +{%- elif DEFAULT_SEQ_NUM is defined %} + suit-manifest-sequence-number: {{ DEFAULT_SEQ_NUM }} +{%- else %} + suit-manifest-sequence-number: 1 +{%- endif %} + suit-common: + suit-components: + - - CAND_MFST + - 0 +{%- if radio is defined %} + {%- set component_index = component_index + 1 %} + {%- set rad_component_index = component_index %} + {{- component_list.append( rad_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_radio_vendor_name }} + name: {{ mpi_radio_class_name }} +{%- endif %} +{%- if application is defined %} + {%- set component_index = component_index + 1 %} + {%- set app_component_index = component_index %} + {{- component_list.append( app_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_application_vendor_name }} + name: {{ mpi_application_class_name }} +{%- endif %} + +{%- set component_list_without_top = component_list[:] %} +{%- if nordic_top %} + {%- set component_index = component_index + 1 %} + {%- set top_component_index = component_index %} + {{- component_list.append( top_component_index ) or ""}} + - - INSTLD_MFST + - RFC4122_UUID: + namespace: nordicsemi.com + name: nRF9280_nordic_top +{%- endif %} + + suit-shared-sequence: + - suit-directive-set-component-index: [{{ component_list|join(',') }}] + - suit-directive-override-parameters: + suit-parameter-vendor-identifier: + RFC4122_UUID: {{ mpi_root_vendor_name }} + suit-parameter-class-identifier: + RFC4122_UUID: + namespace: {{ mpi_root_vendor_name }} + name: {{ mpi_root_class_name }} + - suit-condition-vendor-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-class-identifier: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + suit-dependencies: + # Key is the index of suit-components that describe the dependency manifest + "0": {} +{%- for component_element in component_list %} + "{{ component_element }}": {} +{%- endfor %} + suit-validate: + - suit-directive-set-component-index: [{{ component_list_without_top|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + + suit-invoke: + - suit-directive-set-component-index: [{{ component_list_without_top|join(',') }}] + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + +{%- if APP_ROOT_VERSION is defined %} + suit-current-version: {{ APP_ROOT_VERSION }} +{%- elif DEFAULT_VERSION is defined %} + suit-current-version: {{ DEFAULT_VERSION }} +{%- endif %} + +{%- if main_config['dt'].label2node['suit_storage_partition'].regs[0].size == 24576 %} + # Main DTS contains larger SUIT storage - use legacy encoding + suit-install-legacy: +{%- else %} + suit-install: +{%- endif %} + - suit-directive-set-component-index: 0 +{%- if radio is defined %} + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ radio['name'] }}' + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if application is defined %} + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ application['name'] }}' + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if nordic_top %} + - suit-directive-override-parameters: + suit-parameter-uri: '#top' + - suit-directive-fetch: + - suit-send-record-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} + + suit-candidate-verification: + - suit-directive-set-component-index: 0 +{%- if radio is defined %} + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ radio['name'] }}' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ artifacts_folder ~ radio['name'] }}.suit + - suit-directive-fetch: + - suit-send-record-failure + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if application is defined %} + - suit-directive-override-parameters: + suit-parameter-uri: '#{{ application['name'] }}' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ artifacts_folder ~ application['name'] }}.suit + - suit-directive-fetch: + - suit-send-record-failure + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} +{%- if nordic_top %} + - suit-directive-override-parameters: + suit-parameter-uri: '#top' + suit-parameter-image-digest: + suit-digest-algorithm-id: cose-alg-sha-256 + suit-digest-bytes: + envelope: {{ sysbuild['config']['SB_CONFIG_SUIT_ENVELOPE_NORDIC_TOP_DIRECTORY'] }}/nordic_top.suit + - suit-directive-fetch: + - suit-send-record-failure + # In the case of streaming operations it is worth to retry them at least once. + # This increases the robustness against bit flips on the transport, + # for example when storing the data on an external memory device. + # The suit-directive-try-each will complete on the first successful subsequence. + - suit-directive-try-each: + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - - suit-condition-image-match: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-condition-dependency-integrity: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure + - suit-directive-process-dependency: + - suit-send-record-success + - suit-send-record-failure + - suit-send-sysinfo-success + - suit-send-sysinfo-failure +{%- endif %} + + suit-manifest-component-id: + - INSTLD_MFST + - RFC4122_UUID: + namespace: {{ mpi_root_vendor_name }} + name: {{ mpi_root_class_name }} + suit-integrated-dependencies: +{%- if radio is defined %} + '#{{ radio['name'] }}': {{ artifacts_folder ~ radio['name'] }}.suit +{%- endif %} +{%- if application is defined %} + '#{{ application['name'] }}': {{ artifacts_folder ~ application['name'] }}.suit +{%- endif %} +{%- if nordic_top %} + '#top': {{ sysbuild['config']['SB_CONFIG_SUIT_ENVELOPE_NORDIC_TOP_DIRECTORY'] }}/nordic_top.suit +{%- endif %} diff --git a/subsys/nrf_security/Kconfig b/subsys/nrf_security/Kconfig index 0064d04df591..db18bf41e988 100644 --- a/subsys/nrf_security/Kconfig +++ b/subsys/nrf_security/Kconfig @@ -58,6 +58,7 @@ if NRF_SECURITY config MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS bool + default y if SOC_SERIES_NRF92X && (SOC_NRF9280_CPUSEC || SOC_NRF9230_ENGB_CPUSEC) default y if SOC_SERIES_NRF54LX && PSA_CRYPTO_DRIVER_CRACEN default y if SOC_SERIES_NRF54HX && SOC_NRF54H20_CPUSEC help @@ -258,4 +259,11 @@ endchoice config SOC_NRF54H20_CPUSEC bool +# This is for internal use only. +config SOC_NRF9280_CPUSEC + bool + +config SOC_NRF9230_ENGB_CPUSEC + bool + endmenu diff --git a/subsys/nrf_security/src/drivers/cracen/Kconfig b/subsys/nrf_security/src/drivers/cracen/Kconfig index 8c323f0d9b05..38e9e663416d 100644 --- a/subsys/nrf_security/src/drivers/cracen/Kconfig +++ b/subsys/nrf_security/src/drivers/cracen/Kconfig @@ -5,7 +5,7 @@ # config CRACEN_HW_PRESENT - def_bool SOC_SERIES_NRF54LX + def_bool SOC_SERIES_NRF54LX || SOC_SERIES_NRF92X # Configure CRACEN_LOG_LEVEL module = CRACEN @@ -16,7 +16,8 @@ if PSA_CRYPTO_DRIVER_CRACEN config CRACEN_LOAD_MICROCODE bool "Load CRACEN microcode" - depends on (SOC_SERIES_NRF54LX && !SOC_NRF54L20) || SOC_SERIES_NRF54HX + depends on (SOC_SERIES_NRF54LX && !SOC_NRF54L20) || SOC_SERIES_NRF54HX || \ + (SOC_SERIES_NRF92X && (SOC_NRF9280_CPUSEC || SOC_NRF9230_ENGB_CPUSEC)) default y help Prompt-less configuration to load the CRACEN microcode. diff --git a/subsys/nrf_security/src/drivers/cracen/psa_driver.Kconfig b/subsys/nrf_security/src/drivers/cracen/psa_driver.Kconfig index bd989b5c4a7e..1eb04a228a1c 100644 --- a/subsys/nrf_security/src/drivers/cracen/psa_driver.Kconfig +++ b/subsys/nrf_security/src/drivers/cracen/psa_driver.Kconfig @@ -1848,7 +1848,8 @@ config PSA_NEED_CRACEN_PLATFORM_KEYS default y depends on PSA_WANT_ALG_GCM depends on PSA_WANT_AES_KEY_SIZE_256 - depends on SOC_NRF54H20_CPUSEC + depends on SOC_NRF54H20_CPUSEC || \ + (SOC_NRF9280_CPUSEC || SOC_NRF9230_ENGB_CPUSEC) endmenu diff --git a/subsys/suit/mci/CMakeLists.txt b/subsys/suit/mci/CMakeLists.txt index b0807dbb58cb..538334bbdc72 100644 --- a/subsys/suit/mci/CMakeLists.txt +++ b/subsys/suit/mci/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries(suit_mci INTERFACE suit_metadata) zephyr_library() zephyr_library_sources_ifdef(CONFIG_SUIT_MCI_IMPL_NRF54H20_SDFW src/suit_mci_nrf54h20.c) +zephyr_library_sources_ifdef(CONFIG_SUIT_MCI_IMPL_NRF9280_SDFW src/suit_mci_nrf9280.c) zephyr_library_sources(src/suit_generic_ids.c) zephyr_library_link_libraries(suit_mci) diff --git a/subsys/suit/mci/Kconfig b/subsys/suit/mci/Kconfig index 4f61a91e0328..d0d5c97e47bb 100644 --- a/subsys/suit/mci/Kconfig +++ b/subsys/suit/mci/Kconfig @@ -14,12 +14,17 @@ if SUIT_MCI choice SUIT_MCI_IMPL prompt "MCI implementation" default SUIT_MCI_IMPL_NRF54H20_SDFW if SOC_SERIES_NRF54HX - default SUIT_MCI_IMPL_CUSTOM if !SOC_SERIES_NRF54HX + default SUIT_MCI_IMPL_NRF9280_SDFW if SOC_SERIES_NRF92X + default SUIT_MCI_IMPL_CUSTOM if !SOC_SERIES_NRF54HX && !SOC_SERIES_NRF92X config SUIT_MCI_IMPL_NRF54H20_SDFW bool "nRF54H20: Secure domain" depends on SUIT_PLATFORM_VARIANT_SDFW +config SUIT_MCI_IMPL_NRF9280_SDFW + bool "nRF9280: Secure domain" + depends on SUIT_PLATFORM_VARIANT_SDFW + config SUIT_MCI_IMPL_CUSTOM bool "custom" diff --git a/subsys/suit/mci/src/suit_mci_nrf9280.c b/subsys/suit/mci/src/suit_mci_nrf9280.c new file mode 100644 index 000000000000..9c792b270364 --- /dev/null +++ b/subsys/suit/mci/src/suit_mci_nrf9280.c @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include +#include +#include +#ifdef CONFIG_SDFW_LCS +#include +#endif /* CONFIG_SDFW_LCS */ +#include +#include + +#define MANIFEST_PUBKEY_NRF_TOP_GEN0 0x4000BB00 +#define MANIFEST_PUBKEY_SYSCTRL_GEN0 0x40082100 +#define MANIFEST_PUBKEY_OEM_ROOT_GEN0 0x4000AA00 +#define MANIFEST_PUBKEY_APPLICATION_GEN0 0x40022100 +#define MANIFEST_PUBKEY_RADIO_GEN0 0x40032100 +#define MANIFEST_PUBKEY_GEN_RANGE 2 + +#define FWENC_APPLICATION_GEN0 0x40022000 +#define FWENC_RADIOCORE_GEN0 0x40032000 +#define FWENC_SYSCTRL_GEN0 0x40082000 +#define FWENC_GEN_RANGE 1 + +LOG_MODULE_REGISTER(suit_mci_nrf9280, CONFIG_SUIT_LOG_LEVEL); + +mci_err_t suit_mci_supported_manifest_class_ids_get(suit_manifest_class_info_t *class_info, + size_t *size) +{ + if (class_info == NULL || size == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + return suit_storage_mpi_class_ids_get(class_info, size); +} + +mci_err_t suit_mci_invoke_order_get(const suit_manifest_class_id_t **class_id, size_t *size) +{ + if (class_id == NULL || size == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + size_t output_max_size = *size; + size_t output_size = 2; /* Current number of elements on invocation order list */ + + if (output_size > output_max_size) { + return SUIT_PLAT_ERR_SIZE; + } + + suit_execution_mode_t execution_mode = suit_execution_mode_get(); + + switch (execution_mode) { + case EXECUTION_MODE_INVOKE: + if (suit_storage_mpi_class_get(SUIT_MANIFEST_SEC_TOP, &class_id[0]) != + SUIT_PLAT_SUCCESS) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + + if (suit_storage_mpi_class_get(SUIT_MANIFEST_APP_ROOT, &class_id[1]) != + SUIT_PLAT_SUCCESS) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + break; + + case EXECUTION_MODE_INVOKE_FOREGROUND_DFU: + case EXECUTION_MODE_INVOKE_RECOVERY: + if (suit_storage_mpi_class_get(SUIT_MANIFEST_SEC_TOP, &class_id[0]) != + SUIT_PLAT_SUCCESS) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + + suit_plat_err_t ret = + suit_storage_mpi_class_get(SUIT_MANIFEST_APP_RECOVERY, &class_id[1]); + + /* If recovery class ID is not configured, use application root manifest + * as the recovery manifest. + */ + if (ret == SUIT_PLAT_ERR_NOT_FOUND) { + ret = suit_storage_mpi_class_get(SUIT_MANIFEST_APP_ROOT, &class_id[1]); + } + + if (ret != SUIT_PLAT_SUCCESS) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + + break; + + default: + return SUIT_PLAT_ERR_INCORRECT_STATE; + } + + *size = output_size; + return SUIT_PLAT_SUCCESS; +} + +mci_err_t suit_mci_downgrade_prevention_policy_get(const suit_manifest_class_id_t *class_id, + suit_downgrade_prevention_policy_t *policy) +{ + suit_storage_mpi_t *mpi = NULL; + + if (class_id == NULL || policy == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + if (suit_storage_mpi_get(class_id, &mpi) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + *policy = + suit_mpi_downgrade_prevention_policy_to_metadata(mpi->downgrade_prevention_policy); + if (*policy == SUIT_DOWNGRADE_PREVENTION_UNKNOWN) { + return SUIT_PLAT_ERR_OUT_OF_BOUNDS; + } + + return SUIT_PLAT_SUCCESS; +} + +mci_err_t suit_mci_independent_update_policy_get(const suit_manifest_class_id_t *class_id, + suit_independent_updateability_policy_t *policy) +{ + suit_storage_mpi_t *mpi = NULL; + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (class_id == NULL || policy == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + if (suit_storage_mpi_get(class_id, &mpi) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + *policy = suit_mpi_independent_updateability_policy_to_metadata( + mpi->independent_updateability_policy); + if (*policy == SUIT_INDEPENDENT_UPDATE_UNKNOWN) { + return SUIT_PLAT_ERR_OUT_OF_BOUNDS; + } + + /* Override independent updateability policy in recovery scenarios: + * If the update candidate was delivered by the recovery firmware, + * it must not update the recovery firmware. + * Invoke modes included, so the recovery firmware may reject the incorrect + * update candidate before resetting the SoC. + */ + switch (suit_execution_mode_get()) { + case EXECUTION_MODE_INVOKE_FOREGROUND_DFU: + case EXECUTION_MODE_INVOKE_RECOVERY: + case EXECUTION_MODE_INSTALL_FOREGROUND_DFU: + case EXECUTION_MODE_INSTALL_RECOVERY: + case EXECUTION_MODE_POST_INVOKE_FOREGROUND_DFU: + case EXECUTION_MODE_POST_INVOKE_RECOVERY: + if ((role == SUIT_MANIFEST_APP_RECOVERY) || (role == SUIT_MANIFEST_RAD_RECOVERY)) { + *policy = SUIT_INDEPENDENT_UPDATE_DENIED; + } + break; + + case EXECUTION_MODE_FAIL_INSTALL_NORDIC_TOP: + /* In this state, only the Nordic top manifest is accepted as an update candidate.*/ + if (role != SUIT_MANIFEST_SEC_TOP) { + *policy = SUIT_INDEPENDENT_UPDATE_DENIED; + } + break; + + default: + break; + } + + return SUIT_PLAT_SUCCESS; +} + +mci_err_t suit_mci_manifest_class_id_validate(const suit_manifest_class_id_t *class_id) +{ + if (class_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + suit_plat_err_t ret = suit_storage_mpi_role_get(class_id, &role); + + if (ret != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + return SUIT_PLAT_SUCCESS; +} + +static bool skip_validation(suit_manifest_role_t role) +{ +#ifdef CONFIG_SDFW_LCS + /* Read the domain-specific LCS value. */ + enum lcs current_lcs = LCS_DISCARDED; + + switch (role) { + case SUIT_MANIFEST_SEC_TOP: + case SUIT_MANIFEST_SEC_SDFW: + case SUIT_MANIFEST_SEC_SYSCTRL: + return false; + + case SUIT_MANIFEST_APP_ROOT: + case SUIT_MANIFEST_APP_RECOVERY: + case SUIT_MANIFEST_APP_LOCAL_1: + case SUIT_MANIFEST_APP_LOCAL_2: + case SUIT_MANIFEST_APP_LOCAL_3: + current_lcs = lcs_get(LCS_DOMAIN_ID_APPLICATION); + break; + + case SUIT_MANIFEST_RAD_RECOVERY: + case SUIT_MANIFEST_RAD_LOCAL_1: + case SUIT_MANIFEST_RAD_LOCAL_2: + current_lcs = lcs_get(LCS_DOMAIN_ID_RADIOCORE); + break; + + default: + return false; + } + + if ((current_lcs == LCS_ROT) || (current_lcs == LCS_ROT_DEBUG) || + (current_lcs == LCS_EMPTY)) { + return true; + } +#endif /* CONFIG_SDFW_LCS */ + + return false; +} + +mci_err_t suit_mci_signing_key_id_validate(const suit_manifest_class_id_t *class_id, + uint32_t key_id) +{ + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (class_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + if (key_id == 0) { + suit_storage_mpi_t *mpi = NULL; + + if (skip_validation(role)) { + return SUIT_PLAT_SUCCESS; + } + + if (suit_storage_mpi_get(class_id, &mpi) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + if (mpi->signature_verification_policy == SUIT_MPI_SIGNATURE_CHECK_DISABLED) { + return SUIT_PLAT_SUCCESS; + } else if ((mpi->signature_verification_policy == + SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE) && + suit_execution_mode_booting()) { + /* By allowing key_id == 0 in the invoke path, the platform will verify + * the signature only during updates. + */ + return SUIT_PLAT_SUCCESS; + } + + return MCI_ERR_WRONGKEYID; + } + + switch (role) { + case SUIT_MANIFEST_SEC_TOP: + case SUIT_MANIFEST_SEC_SDFW: + if (key_id >= MANIFEST_PUBKEY_NRF_TOP_GEN0 && + key_id <= MANIFEST_PUBKEY_NRF_TOP_GEN0 + MANIFEST_PUBKEY_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_SEC_SYSCTRL: + if (key_id >= MANIFEST_PUBKEY_SYSCTRL_GEN0 && + key_id <= MANIFEST_PUBKEY_SYSCTRL_GEN0 + MANIFEST_PUBKEY_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_APP_ROOT: + if (key_id >= MANIFEST_PUBKEY_OEM_ROOT_GEN0 && + key_id <= MANIFEST_PUBKEY_OEM_ROOT_GEN0 + MANIFEST_PUBKEY_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_APP_RECOVERY: + case SUIT_MANIFEST_APP_LOCAL_1: + case SUIT_MANIFEST_APP_LOCAL_2: + case SUIT_MANIFEST_APP_LOCAL_3: + if (key_id >= MANIFEST_PUBKEY_APPLICATION_GEN0 && + key_id <= MANIFEST_PUBKEY_APPLICATION_GEN0 + MANIFEST_PUBKEY_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_RAD_RECOVERY: + case SUIT_MANIFEST_RAD_LOCAL_1: + case SUIT_MANIFEST_RAD_LOCAL_2: + if (key_id >= MANIFEST_PUBKEY_RADIO_GEN0 && + key_id <= MANIFEST_PUBKEY_RADIO_GEN0 + MANIFEST_PUBKEY_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + default: + break; + } + + return MCI_ERR_WRONGKEYID; +} + +mci_err_t suit_mci_fw_encryption_key_id_validate(const suit_manifest_class_id_t *class_id, + uint32_t key_id) +{ + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (class_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + switch (role) { + case SUIT_MANIFEST_SEC_SYSCTRL: + if (key_id >= FWENC_SYSCTRL_GEN0 && + key_id <= FWENC_SYSCTRL_GEN0 + FWENC_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_APP_RECOVERY: + case SUIT_MANIFEST_APP_LOCAL_1: + case SUIT_MANIFEST_APP_LOCAL_2: + case SUIT_MANIFEST_APP_LOCAL_3: + if (key_id >= FWENC_APPLICATION_GEN0 && + key_id <= FWENC_APPLICATION_GEN0 + FWENC_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_RAD_RECOVERY: + case SUIT_MANIFEST_RAD_LOCAL_1: + case SUIT_MANIFEST_RAD_LOCAL_2: + if (key_id >= FWENC_RADIOCORE_GEN0 && + key_id <= FWENC_RADIOCORE_GEN0 + FWENC_GEN_RANGE) { + return SUIT_PLAT_SUCCESS; + } + break; + + default: + break; + } + + return MCI_ERR_WRONGKEYID; +} + +mci_err_t suit_mci_processor_start_rights_validate(const suit_manifest_class_id_t *class_id, + int processor_id) +{ + if (class_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + switch (role) { + case SUIT_MANIFEST_UNKNOWN: + return MCI_ERR_MANIFESTCLASSID; + + case SUIT_MANIFEST_SEC_TOP: + case SUIT_MANIFEST_APP_ROOT: + case SUIT_MANIFEST_SEC_SDFW: + break; + + case SUIT_MANIFEST_SEC_SYSCTRL: + /* Sys manifest */ + if (processor_id == NRF_PROCESSOR_SYSCTRL) { + /* SysCtrl */ + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_APP_RECOVERY: + case SUIT_MANIFEST_APP_LOCAL_1: + case SUIT_MANIFEST_APP_LOCAL_2: + case SUIT_MANIFEST_APP_LOCAL_3: + /* App manifest */ + if (processor_id == NRF_PROCESSOR_APPLICATION) { + /* Appcore */ + return SUIT_PLAT_SUCCESS; + } + break; + + case SUIT_MANIFEST_RAD_RECOVERY: + case SUIT_MANIFEST_RAD_LOCAL_1: + case SUIT_MANIFEST_RAD_LOCAL_2: + /* Rad manifest */ + if (processor_id == NRF_PROCESSOR_RADIOCORE) { + /* Radiocore */ + return SUIT_PLAT_SUCCESS; + } + break; + + default: + break; + } + + return MCI_ERR_NOACCESS; +} + +mci_err_t suit_mci_memory_access_rights_validate(const suit_manifest_class_id_t *class_id, + void *address, size_t mem_size) +{ + if (class_id == NULL || address == NULL || mem_size == 0) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + /* If the SUIT orchestrator is currently processing update candidate, + * block all MEM components (regardless of the manifest class ID) + * that points to the DFU partition, or any other region that contains + * update candidate. + * This check is necessary to ensure that the digest of the + * authenticated digest of manifest or involved new firmware components + * remains unchanged during the whole update procedure. + */ + if (suit_execution_mode_updating()) { + const suit_plat_mreg_t *update_regions = NULL; + size_t update_regions_len = 0; + suit_plat_err_t plat_err = + suit_storage_update_cand_get(&update_regions, &update_regions_len); + + if (plat_err != SUIT_PLAT_SUCCESS) { + /* Should never happen as the execution mode indicates processing of + * a pending update candiadate. + */ + return SUIT_PLAT_ERR_CRASH; + } + + /* Ensure that the regions are mutually exclusive. + * + * The condition below is a negation of the following condition: + * (start_a) >= (start_b + size_b) + * or + * (start_b) >= (start_a + size_a) + */ + for (size_t i = 0; i < update_regions_len; i++) { + if ((((uint8_t *)address) < + (update_regions[i].mem + update_regions[i].size)) && + (update_regions[i].mem < (((uint8_t *)address) + mem_size))) { + return MCI_ERR_NOACCESS; + } + } + } + + switch (role) { + case SUIT_MANIFEST_UNKNOWN: + return MCI_ERR_MANIFESTCLASSID; + + case SUIT_MANIFEST_SEC_TOP: + /* Nordic top manifest - ability to operate on memory ranges intentionally blocked + */ + return MCI_ERR_NOACCESS; + + case SUIT_MANIFEST_SEC_SDFW: + /* SDFW & SDFW Recovery manifest - ability to operate on memory ranges intentionally + * blocked. + */ + return MCI_ERR_NOACCESS; + + case SUIT_MANIFEST_SEC_SYSCTRL: + /* Sysctrl manifest - TODO - implement checks based on UICR/SICR + */ + return SUIT_PLAT_SUCCESS; + + case SUIT_MANIFEST_APP_ROOT: + /* Root manifest - ability to operate on memory ranges intentionally blocked + */ + return MCI_ERR_NOACCESS; + + case SUIT_MANIFEST_APP_RECOVERY: + case SUIT_MANIFEST_APP_LOCAL_1: + case SUIT_MANIFEST_APP_LOCAL_2: + case SUIT_MANIFEST_APP_LOCAL_3: { + struct arbiter_mem_params_access mem_params = { + .allowed_types = ARBITER_MEM_TYPE(RESERVED, FIXED), + .access = { + .owner = NRF_OWNER_APPLICATION, + .permissions = ARBITER_MEM_PERM(READ, SECURE), + .address = (uintptr_t)address, + .size = mem_size, + }, + }; + + if (arbiter_mem_access_check(&mem_params) != ARBITER_STATUS_OK) { + return MCI_ERR_NOACCESS; + } + + return SUIT_PLAT_SUCCESS; + } + + case SUIT_MANIFEST_RAD_RECOVERY: + case SUIT_MANIFEST_RAD_LOCAL_1: + case SUIT_MANIFEST_RAD_LOCAL_2: { + struct arbiter_mem_params_access mem_params = { + .allowed_types = ARBITER_MEM_TYPE(RESERVED, FIXED), + .access = { + .owner = NRF_OWNER_RADIOCORE, + .permissions = ARBITER_MEM_PERM(READ, SECURE), + .address = (uintptr_t)address, + .size = mem_size, + }, + }; + + if (arbiter_mem_access_check(&mem_params) != ARBITER_STATUS_OK) { + return MCI_ERR_NOACCESS; + } + + return SUIT_PLAT_SUCCESS; + } + + default: + break; + } + + return MCI_ERR_NOACCESS; +} + +mci_err_t +suit_mci_platform_specific_component_rights_validate(const suit_manifest_class_id_t *class_id, + int platform_specific_component_number) +{ + if (class_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_manifest_role_t role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(class_id, &role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + if (role == SUIT_MANIFEST_SEC_SDFW) { + /* The only manifest with ability to control platform specific components is secdom. + * 1 - SDFW + * 2 - SDFW Recovery + */ + if (platform_specific_component_number == 1 || + platform_specific_component_number == 2) { + return SUIT_PLAT_SUCCESS; + } + } + + return MCI_ERR_NOACCESS; +} + +mci_err_t suit_mci_vendor_id_for_manifest_class_id_get(const suit_manifest_class_id_t *class_id, + const suit_uuid_t **vendor_id) +{ + suit_storage_mpi_t *mpi = NULL; + + if (class_id == NULL || vendor_id == NULL) { + return SUIT_PLAT_ERR_INVAL; + } + + if (suit_storage_mpi_get(class_id, &mpi) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + /* Casting is done as a temporary solution until mpi refactoring */ + *vendor_id = (const suit_uuid_t *)mpi->vendor_id; + return SUIT_PLAT_SUCCESS; +} + +mci_err_t +suit_mci_manifest_parent_child_declaration_validate(const suit_manifest_class_id_t *parent_class_id, + const suit_manifest_class_id_t *child_class_id) +{ + if ((parent_class_id == NULL) || (child_class_id == NULL)) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_manifest_role_t parent_role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(parent_class_id, &parent_role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + suit_manifest_role_t child_role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(child_class_id, &child_role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + if ((parent_role == SUIT_MANIFEST_APP_ROOT) && + (((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)) || + (child_role == SUIT_MANIFEST_SEC_TOP))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_RECOVERY) && + ((child_role == SUIT_MANIFEST_RAD_RECOVERY) || + ((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)))) { + return SUIT_PLAT_SUCCESS; + } + + return MCI_ERR_NOACCESS; +} + +mci_err_t +suit_mci_manifest_process_dependency_validate(const suit_manifest_class_id_t *parent_class_id, + const suit_manifest_class_id_t *child_class_id) +{ + if ((parent_class_id == NULL) || (child_class_id == NULL)) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_execution_mode_t execution_mode = suit_execution_mode_get(); + + suit_manifest_role_t parent_role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(parent_class_id, &parent_role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + suit_manifest_role_t child_role = SUIT_MANIFEST_UNKNOWN; + + if (suit_storage_mpi_role_get(child_class_id, &child_role) != SUIT_PLAT_SUCCESS) { + return MCI_ERR_MANIFESTCLASSID; + } + + switch (execution_mode) { + case EXECUTION_MODE_INVOKE: + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || + (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_ROOT) && + (((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)))) { + return SUIT_PLAT_SUCCESS; + } + break; + + case EXECUTION_MODE_INSTALL: + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || + (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_ROOT) && + (((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)) || + (child_role == SUIT_MANIFEST_SEC_TOP))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_RECOVERY) && + ((child_role == SUIT_MANIFEST_RAD_RECOVERY) || + ((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)))) { + return SUIT_PLAT_SUCCESS; + } + + break; + + case EXECUTION_MODE_INSTALL_FOREGROUND_DFU: + case EXECUTION_MODE_INSTALL_RECOVERY: + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || + (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_ROOT) && + (((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)) || + (child_role == SUIT_MANIFEST_SEC_TOP))) { + return SUIT_PLAT_SUCCESS; + } + break; + + case EXECUTION_MODE_INVOKE_FOREGROUND_DFU: + case EXECUTION_MODE_INVOKE_RECOVERY: + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || + (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_RECOVERY) && + ((child_role == SUIT_MANIFEST_RAD_RECOVERY) || + ((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)))) { + return SUIT_PLAT_SUCCESS; + } + + if ((parent_role == SUIT_MANIFEST_APP_ROOT) && + (((child_role >= SUIT_MANIFEST_APP_LOCAL_1) && + (child_role <= SUIT_MANIFEST_APP_LOCAL_3)) || + ((child_role >= SUIT_MANIFEST_RAD_LOCAL_1) && + (child_role <= SUIT_MANIFEST_RAD_LOCAL_2)))) { + return SUIT_PLAT_SUCCESS; + } + break; + + case EXECUTION_MODE_FAIL_INSTALL_NORDIC_TOP: + /* In this state it is prohibited to process OEM manifests. */ + if ((parent_role == SUIT_MANIFEST_SEC_TOP) && + ((child_role == SUIT_MANIFEST_SEC_SYSCTRL) || + (child_role == SUIT_MANIFEST_SEC_SDFW))) { + return SUIT_PLAT_SUCCESS; + } + break; + + default: + break; + } + + return MCI_ERR_NOACCESS; +} + +mci_err_t suit_mci_init(void) +{ + return SUIT_PLAT_SUCCESS; +} diff --git a/subsys/suit/memory_layout/Kconfig b/subsys/suit/memory_layout/Kconfig index 268bc31f0b82..6c1f681e89ef 100644 --- a/subsys/suit/memory_layout/Kconfig +++ b/subsys/suit/memory_layout/Kconfig @@ -6,7 +6,7 @@ config SUIT_MEMORY_LAYOUT_EXTMEM_ADDRESS_RANGE_START hex - default 0x60000000 if SOC_SERIES_NRF54HX + default 0x60000000 if SOC_SERIES_NRF54HX || SOC_SERIES_NRF92X default 0x12000000 if SOC_NRF52840 default 0x0 if BOARD_NATIVE_SIM help @@ -15,7 +15,7 @@ config SUIT_MEMORY_LAYOUT_EXTMEM_ADDRESS_RANGE_START config SUIT_MEMORY_LAYOUT_EXTMEM_ADDRESS_RANGE_SIZE hex - default 0x40000000 if SOC_SERIES_NRF54HX + default 0x40000000 if SOC_SERIES_NRF54HX || SOC_SERIES_NRF92X default 0x8000000 if SOC_NRF52840 default 0x0 if BOARD_NATIVE_SIM help diff --git a/subsys/suit/metadata/include/suit_metadata.h b/subsys/suit/metadata/include/suit_metadata.h index e976b6304f0d..87869079c8da 100644 --- a/subsys/suit/metadata/include/suit_metadata.h +++ b/subsys/suit/metadata/include/suit_metadata.h @@ -107,6 +107,9 @@ typedef enum { SUIT_MANIFEST_RAD_LOCAL_1 = 0x31, /** Manifest describes OEM-specific binaries, specific for radio core. */ SUIT_MANIFEST_RAD_LOCAL_2 = 0x32, + + /** Manifest describes Cellular Domain firmware update and boot procedures. */ + SUIT_MANIFEST_NORDIC_CELLFW = 0x41, } suit_manifest_role_t; /* Manifest domain nibble. */ @@ -122,6 +125,9 @@ typedef enum { /** Manifest domain for Radio-controlled manifests. */ SUIT_MANIFEST_DOMAIN_RAD = 0x30, + + /** Manifest domain for Nordic-controlled manifests (Cellular). */ + SUIT_MANIFEST_DOMAIN_NORDIC_CELL = 0x40, } suit_manifest_domain_t; /** The 128-bit UUID, used for identifying vendors as well as classes. */ diff --git a/subsys/suit/platform/Kconfig b/subsys/suit/platform/Kconfig index e56bfbeac105..41c85e96af58 100644 --- a/subsys/suit/platform/Kconfig +++ b/subsys/suit/platform/Kconfig @@ -26,6 +26,7 @@ config MAX_NUMBER_OF_MANIFEST_CLASS_IDS int "Maximum number of supported manifest class IDs that can be handled" range 1 200 default 11 if SOC_SERIES_NRF54HX + default 13 if SOC_SERIES_NRF92X default 5 if SOC_NRF52840 default 5 if SOC_POSIX help diff --git a/subsys/suit/platform/sdfw/src/suit_plat_authenticate.c b/subsys/suit/platform/sdfw/src/suit_plat_authenticate.c index 40e90105637d..92fbdcf720d0 100644 --- a/subsys/suit/platform/sdfw/src/suit_plat_authenticate.c +++ b/subsys/suit/platform/sdfw/src/suit_plat_authenticate.c @@ -68,6 +68,8 @@ int suit_plat_authenticate_manifest(struct zcbor_string *manifest_component_id, return SUIT_ERR_UNSUPPORTED_PARAMETER; } + LOG_WRN("Decoded key ID: %08x", public_key_id); + /* Validate KEY ID */ ret = suit_mci_signing_key_id_validate(class_id, public_key_id); if (ret != SUIT_PLAT_SUCCESS) { diff --git a/subsys/suit/platform/sdfw/src/suit_plat_check_image_match_sdfw_specific.c b/subsys/suit/platform/sdfw/src/suit_plat_check_image_match_sdfw_specific.c index 67bf4f254c9e..accae7382ccc 100644 --- a/subsys/suit/platform/sdfw/src/suit_plat_check_image_match_sdfw_specific.c +++ b/subsys/suit/platform/sdfw/src/suit_plat_check_image_match_sdfw_specific.c @@ -19,7 +19,7 @@ static int suit_plat_check_image_match_soc_spec_sdfw(struct zcbor_string *compon enum suit_cose_alg alg_id, struct zcbor_string *digest) { -#ifdef CONFIG_SOC_SERIES_NRF54HX +#if defined(CONFIG_SOC_SERIES_NRF54HX) || defined(CONFIG_SOC_SERIES_NRF92X) if (suit_cose_sha512 != alg_id) { LOG_ERR("Unsupported digest algorithm: %d", alg_id); return SUIT_ERR_UNSUPPORTED_PARAMETER; @@ -40,9 +40,9 @@ static int suit_plat_check_image_match_soc_spec_sdfw(struct zcbor_string *compon } return SUIT_SUCCESS; -#else /* CONFIG_SOC_SERIES_NRF54HX */ +#else /* CONFIG_SOC_SERIES_NRF54HX || CONFIG_SOC_SERIES_NRF92X */ return SUIT_ERR_UNSUPPORTED_COMPONENT_ID; -#endif /* CONFIG_SOC_SERIES_NRF54HX */ +#endif /* CONFIG_SOC_SERIES_NRF54HX || CONFIG_SOC_SERIES_NRF92X */ } static int suit_plat_check_image_match_soc_spec(struct zcbor_string *component_id, diff --git a/subsys/suit/storage/CMakeLists.txt b/subsys/suit/storage/CMakeLists.txt index 768285d7b190..5f2a7510424d 100644 --- a/subsys/suit/storage/CMakeLists.txt +++ b/subsys/suit/storage/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(suit_storage_interface INTERFACE suit_utils) zephyr_library() zephyr_library_sources_ifdef(CONFIG_SUIT_STORAGE_LAYOUT_TEST src/suit_storage_test.c) zephyr_library_sources_ifdef(CONFIG_SUIT_STORAGE_LAYOUT_NRF54H20 src/suit_storage_nrf54h20.c) +zephyr_library_sources_ifdef(CONFIG_SUIT_STORAGE_LAYOUT_NRF9280 src/suit_storage_nrf9280.c) zephyr_library_sources(src/suit_storage_update.c) zephyr_library_sources(src/suit_storage_encode.c) zephyr_library_sources(src/suit_storage_envelope.c) diff --git a/subsys/suit/storage/Kconfig b/subsys/suit/storage/Kconfig index 7ec33016c228..dd01e77be202 100644 --- a/subsys/suit/storage/Kconfig +++ b/subsys/suit/storage/Kconfig @@ -18,12 +18,18 @@ if SUIT_STORAGE choice SUIT_STORAGE_LAYOUT prompt "MCU-specific layout of the SUIT storage" default SUIT_STORAGE_LAYOUT_NRF54H20 if SOC_SERIES_NRF54HX + default SUIT_STORAGE_LAYOUT_NRF9280 if SOC_SERIES_NRF92X default SUIT_STORAGE_LAYOUT_TEST if !SOC_SERIES_NRF54HX config SUIT_STORAGE_LAYOUT_NRF54H20 bool "nRF54H20" select PSA_WANT_ALG_SHA_256 if SUIT_CRYPTO +config SUIT_STORAGE_LAYOUT_NRF9280 + bool "nRF9280" + depends on SUIT_CRYPTO + select PSA_WANT_ALG_SHA_256 + config SUIT_STORAGE_LAYOUT_TEST bool "tests" help @@ -40,6 +46,7 @@ config SUIT_STORAGE_N_UPDATE_REGIONS config SUIT_STORAGE_N_ENVELOPES int "Number of envelopes, possible to store in SUIT storage" default 11 if SOC_SERIES_NRF54HX + default 13 if SOC_SERIES_NRF92X default 5 if SOC_NRF52840 default 5 if SOC_POSIX help diff --git a/subsys/suit/storage/src/suit_storage_mpi.c b/subsys/suit/storage/src/suit_storage_mpi.c index 427659b415f2..5c2b4a4ffbfa 100644 --- a/subsys/suit/storage/src/suit_storage_mpi.c +++ b/subsys/suit/storage/src/suit_storage_mpi.c @@ -59,6 +59,8 @@ suit_plat_err_t suit_storage_mpi_configuration_load(suit_manifest_role_t role, c const suit_manifest_class_id_t *new_class_id = NULL; suit_storage_mpi_t *mpi = (suit_storage_mpi_t *)addr; + LOG_INF("Suit manifest role: %02x", role); + if ((role == SUIT_MANIFEST_UNKNOWN) || (addr == NULL) || (size == 0)) { return SUIT_PLAT_ERR_INVAL; } @@ -113,7 +115,10 @@ suit_plat_err_t suit_storage_mpi_configuration_load(suit_manifest_role_t role, c /* Validate downgrade prevention policy value. */ switch (mpi->downgrade_prevention_policy) { case SUIT_MPI_DOWNGRADE_PREVENTION_DISABLED: + LOG_INF("Downgrade prevention policy disabled"); + break; case SUIT_MPI_DOWNGRADE_PREVENTION_ENABLED: + LOG_INF("Downgrade prevention policy enabled"); break; default: LOG_ERR("Invalid downgrade prevention policy value for role 0x%x%s: %d", role, @@ -141,8 +146,13 @@ suit_plat_err_t suit_storage_mpi_configuration_load(suit_manifest_role_t role, c /* Validate signature verification policy value. */ switch (mpi->signature_verification_policy) { case SUIT_MPI_SIGNATURE_CHECK_DISABLED: + LOG_INF("Signature check disabled"); + break; case SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE: + LOG_INF("Signature check enabled on update"); + break; case SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE_AND_BOOT: + LOG_INF("Signature check enabled on update and boot"); break; default: LOG_ERR("Invalid signature verification policy value for role 0x%x%s: %d", role, diff --git a/subsys/suit/storage/src/suit_storage_nrf9280.c b/subsys/suit/storage/src/suit_storage_nrf9280.c new file mode 100644 index 000000000000..11c5d2781830 --- /dev/null +++ b/subsys/suit/storage/src/suit_storage_nrf9280.c @@ -0,0 +1,1241 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_SUIT_CRYPTO +#include +#endif /* CONFIG_SUIT_CRYPTO */ + +LOG_MODULE_REGISTER(suit_storage, CONFIG_SUIT_LOG_LEVEL); + +/* SUIT storage partition is defined as reserved-memory region, thus it uses absolute addresses. */ +#define SUIT_STORAGE_OFFSET \ + (FIXED_PARTITION_OFFSET(suit_storage_partition) - DT_REG_ADDR(DT_CHOSEN(zephyr_flash))) +#define SUIT_STORAGE_SIZE FIXED_PARTITION_SIZE(suit_storage_partition) +#define SUIT_STORAGE_NORDIC_ADDRESS suit_plat_mem_nvm_ptr_get(SUIT_STORAGE_NORDIC_OFFSET) +#define SUIT_STORAGE_NORDIC_OFFSET FIXED_PARTITION_OFFSET(cpusec_suit_storage) +#define SUIT_STORAGE_NORDIC_SIZE FIXED_PARTITION_SIZE(cpusec_suit_storage) +#define SUIT_STORAGE_NORDIC_CELL_ADDRESS suit_plat_mem_nvm_ptr_get(SUIT_STORAGE_NORDIC_CELL_OFFSET) +#define SUIT_STORAGE_NORDIC_CELL_OFFSET FIXED_PARTITION_OFFSET(cpucell_suit_storage) +#define SUIT_STORAGE_NORDIC_CELL_SIZE FIXED_PARTITION_SIZE(cpucell_suit_storage) +#define SUIT_STORAGE_RAD_ADDRESS suit_plat_mem_nvm_ptr_get(SUIT_STORAGE_RAD_OFFSET) +#define SUIT_STORAGE_RAD_OFFSET FIXED_PARTITION_OFFSET(cpurad_suit_storage) +#define SUIT_STORAGE_RAD_SIZE FIXED_PARTITION_SIZE(cpurad_suit_storage) +#define SUIT_STORAGE_APP_ADDRESS suit_plat_mem_nvm_ptr_get(SUIT_STORAGE_APP_OFFSET) +#define SUIT_STORAGE_APP_OFFSET FIXED_PARTITION_OFFSET(cpuapp_suit_storage) +#define SUIT_STORAGE_APP_SIZE FIXED_PARTITION_SIZE(cpuapp_suit_storage) + +BUILD_ASSERT((SUIT_STORAGE_OFFSET <= SUIT_STORAGE_NORDIC_OFFSET) && + (SUIT_STORAGE_NORDIC_OFFSET + SUIT_STORAGE_NORDIC_SIZE <= + SUIT_STORAGE_OFFSET + SUIT_STORAGE_SIZE), + "Secure storage must be defined within SUIT storage partition"); + +BUILD_ASSERT((SUIT_STORAGE_OFFSET <= SUIT_STORAGE_NORDIC_CELL_OFFSET) && + (SUIT_STORAGE_NORDIC_CELL_OFFSET + SUIT_STORAGE_NORDIC_CELL_SIZE <= + SUIT_STORAGE_OFFSET + SUIT_STORAGE_SIZE), + "Cellular storage must be defined within SUIT storage partition"); + +BUILD_ASSERT((SUIT_STORAGE_OFFSET <= SUIT_STORAGE_RAD_OFFSET) && + (SUIT_STORAGE_RAD_OFFSET + SUIT_STORAGE_RAD_SIZE <= + SUIT_STORAGE_OFFSET + SUIT_STORAGE_SIZE), + "Radiocore storage must be defined within SUIT storage partition"); + +BUILD_ASSERT((SUIT_STORAGE_OFFSET <= SUIT_STORAGE_APP_OFFSET) && + (SUIT_STORAGE_APP_OFFSET + SUIT_STORAGE_APP_SIZE <= + SUIT_STORAGE_OFFSET + SUIT_STORAGE_SIZE), + "Application storage must be defined within SUIT storage partition"); + +typedef uint8_t suit_storage_digest_t[32]; + +struct suit_storage_flags { + /* SUIT boot flags. */ + uint8_t flags[EB_SIZE(suit_storage_flags_t)]; +}; + +struct suit_storage_nvv { + /* Manifest Runtime Non Volatile Variables. */ + uint8_t var[EB_SIZE(suit_storage_nvv_t)]; + /* Manifest Runtime Non Volatile Variables digest. */ + uint8_t digest[EB_SIZE(suit_storage_digest_t)]; +}; + +struct suit_storage_oem_exposed_config { + /* Nordic OEM Exposed SUIT Config. */ + uint8_t nordic_exposed_suit_config_version; + uint8_t reserved_0[15]; + uint8_t modem_manifest_class_id[16]; + uint8_t reserved_1[32]; + /* Nordic OEM Exposed SUIT Config digest. */ + uint8_t digest[EB_SIZE(suit_storage_digest_t)]; +}; + +struct suit_storage_mpi_rad { + /* Radio recovery manifest MPI. */ + uint8_t recovery[EB_SIZE(suit_storage_mpi_t)]; + /* Radio local A manifest MPI. */ + uint8_t local_a[EB_SIZE(suit_storage_mpi_t)]; + /* Radio local B manifest MPI. */ + uint8_t local_b[EB_SIZE(suit_storage_mpi_t)]; + /* Radio MPI digest. */ + uint8_t digest[EB_SIZE(suit_storage_digest_t)]; +}; + +struct suit_storage_mpi_app { + /* Application root manifest MPI. */ + uint8_t root[EB_SIZE(suit_storage_mpi_t)]; + /* Application recovery manifest MPI. */ + uint8_t recovery[EB_SIZE(suit_storage_mpi_t)]; + /* Application local A manifest MPI. */ + uint8_t local_a[EB_SIZE(suit_storage_mpi_t)]; + /* Application local B manifest MPI. */ + uint8_t local_b[EB_SIZE(suit_storage_mpi_t)]; + /* Application local spare manifest MPI. */ + uint8_t local_spare[EB_SIZE(suit_storage_mpi_t)]; + /* Application MPI digest. */ + uint8_t digest[EB_SIZE(suit_storage_digest_t)]; +}; + +/* SUIT storage structure, that can be used to parse the NVM contents. */ +struct suit_storage_nordic { + /* Area accessible by the SECURE core debugger. */ + union { + struct suit_storage_area_nordic { + /* Radio MPI Backup and digest. */ + struct suit_storage_mpi_rad rad_mpi_bak; /* 176 bytes */ + /* Application MPI Backup and digest. */ + struct suit_storage_mpi_app app_mpi_bak; /* 272 bytes */ + + /* Nordic OEM Exposed SUIT Config Backup and digest. */ + struct suit_storage_oem_exposed_config + oem_exposed_config_bak; /* 96 bytes */ + + /* Flags used for selecting the boot path. */ + struct suit_storage_flags flags; /* 16 bytes */ + + /* The last SUIT update report. */ + uint8_t report_0[144]; + + /* Reserved for Future Use. */ + uint8_t reserved[1344]; + + /* Installed envelope: Secure Domain Firmware (update) */ + uint8_t sdfw[EB_ALIGN(1024)]; + /* Installed envelope: System Controller Firmware */ + uint8_t scfw[EB_ALIGN(1024)]; + } nordic; + uint8_t nordic_area[ACCESS_SIZE(struct suit_storage_area_nordic)]; + }; +}; + +/* SUIT storage structure, that can be used to parse the NVM contents. */ +struct suit_storage_nordic_cell { + /* Area accessible by the SECURE core debugger. */ + union { + struct suit_storage_area_nordic_cell { + /* Installed envelope: Nordic Top */ + uint8_t top[EB_ALIGN(1536)]; + /* Installed envelope: LTE/DECT Firmware */ + uint8_t cellfw[EB_ALIGN(1536)]; + /* Installed envelope: iSIM Firmware*/ + uint8_t isimfw[EB_ALIGN(1024)]; + } nordic_cell; + uint8_t nordic_cell_area[ACCESS_SIZE(struct suit_storage_area_nordic_cell)]; + }; +}; + +struct suit_storage_rad { + /* Area accessible by the RADIO core debugger. */ + union { + struct suit_storage_area_rad { + /* Radio manifests provisioning information. */ + struct suit_storage_mpi_rad mpi; + + /* Reserved for Future Use. */ + uint8_t reserved[848]; + + /* Installed envelope: Recovery */ + uint8_t recovery[EB_ALIGN(1024)]; + /* Installed envelope: Local A */ + uint8_t local_a[EB_ALIGN(1024)]; + /* Installed envelope: Local B */ + uint8_t local_b[EB_ALIGN(1024)]; + } rad; + uint8_t rad_area[ACCESS_SIZE(struct suit_storage_area_rad)]; + }; +}; + +struct suit_storage_app { + /* Area accessible by the APP core debugger. */ + union { + struct suit_storage_area_app { + /* Application manifests provisioning information. */ + struct suit_storage_mpi_app mpi; + + /* Reserved for Future Use. */ + uint8_t reserved[464]; + + /* Nordic OEM Exposed SUIT Config and digest. */ + struct suit_storage_oem_exposed_config oem_exposed_config; /* 96 bytes */ + + /** Update candidate information. */ + uint8_t update_cand[EB_SIZE(struct update_candidate_info)]; + + /* Manifest Runtime Non Volatile Variables Area */ + struct suit_storage_nvv nvv; + struct suit_storage_nvv nvv_bak; + + /* Installed envelope: ROOT */ + uint8_t root[EB_ALIGN(2048)]; + /* Installed envelope: Recovery */ + uint8_t recovery[EB_ALIGN(2048)]; + /* Installed envelope: Local A */ + uint8_t local_a[EB_ALIGN(1024)]; + /* Installed envelope: Local B */ + uint8_t local_b[EB_ALIGN(1024)]; + /* Installed envelope: Local Spare RFU */ + uint8_t local_spare[EB_ALIGN(1024)]; + } app; + uint8_t app_area[ACCESS_SIZE(struct suit_storage_area_app)]; + }; +}; + +static const suit_storage_mpi_t mpi_nordic[] = { + { + .version = SUIT_MPI_INFO_VERSION, + .downgrade_prevention_policy = SUIT_MPI_DOWNGRADE_PREVENTION_ENABLED, + .independent_updateability_policy = SUIT_MPI_INDEPENDENT_UPDATE_ALLOWED, + .signature_verification_policy = + SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE_AND_BOOT, + .reserved = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}, + /* RFC4122 uuid5(uuid.NAMESPACE_DNS, 'nordicsemi.com') */ + .vendor_id = {0x76, 0x17, 0xda, 0xa5, 0x71, 0xfd, 0x5a, 0x85, 0x8f, 0x94, 0xe2, + 0x8d, 0x73, 0x5c, 0xe9, 0xf4}, + /* RFC4122 uuid5(nordic_vid, 'nRF9280_nordic_top') */ + .class_id = {0xa9, 0x6d, 0x08, 0xa3, 0x21, 0x8f, 0x5a, 0x9c, 0xa3, 0x9e, 0xea, 0x33, + 0xce, 0x8f, 0x56, 0x50}, + }, + { + .version = SUIT_MPI_INFO_VERSION, + .downgrade_prevention_policy = SUIT_MPI_DOWNGRADE_PREVENTION_ENABLED, + .independent_updateability_policy = SUIT_MPI_INDEPENDENT_UPDATE_DENIED, + .signature_verification_policy = + SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE_AND_BOOT, + .reserved = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}, + /* RFC4122 uuid5(uuid.NAMESPACE_DNS, 'nordicsemi.com') */ + .vendor_id = {0x76, 0x17, 0xda, 0xa5, 0x71, 0xfd, 0x5a, 0x85, 0x8f, 0x94, 0xe2, + 0x8d, 0x73, 0x5c, 0xe9, 0xf4}, + /* RFC4122 uuid5(nordic_vid, 'nRF9280_sec') */ + .class_id = {0xef, 0x05, 0xbe, 0xf3, 0x7d, 0x8b, 0x58, 0xb7, 0xae, 0xdd, 0xd6, 0x90, + 0x4e, 0x86, 0xb6, 0x49}, + }, + { + .version = SUIT_MPI_INFO_VERSION, + .downgrade_prevention_policy = SUIT_MPI_DOWNGRADE_PREVENTION_ENABLED, + .independent_updateability_policy = SUIT_MPI_INDEPENDENT_UPDATE_DENIED, + .signature_verification_policy = + SUIT_MPI_SIGNATURE_CHECK_ENABLED_ON_UPDATE_AND_BOOT, + .reserved = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}, + /* RFC4122 uuid5(uuid.NAMESPACE_DNS, 'nordicsemi.com') */ + .vendor_id = {0x76, 0x17, 0xda, 0xa5, 0x71, 0xfd, 0x5a, 0x85, 0x8f, 0x94, 0xe2, + 0x8d, 0x73, 0x5c, 0xe9, 0xf4}, + /* RFC4122 uuid5(nordic_vid, 'nRF9280_sys') */ + .class_id = {0xe4, 0xa0, 0xb0, 0xd4, 0xbf, 0xff, 0x5a, 0x9d, 0x8f, 0xb1, 0x61, 0xba, + 0xc6, 0xde, 0xc4, 0xbc}, + }}; + +static suit_plat_err_t find_manifest_area(suit_manifest_role_t role, const uint8_t **addr, + size_t *size) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + struct suit_storage_nordic_cell *nordic_storage_cell = + (struct suit_storage_nordic_cell *)SUIT_STORAGE_NORDIC_CELL_ADDRESS; + struct suit_storage_rad *rad_storage = (struct suit_storage_rad *)SUIT_STORAGE_RAD_ADDRESS; + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + + switch (role) { + case SUIT_MANIFEST_SEC_TOP: + *addr = nordic_storage_cell->nordic_cell.top; + *size = sizeof(nordic_storage_cell->nordic_cell.top); + break; + case SUIT_MANIFEST_SEC_SDFW: + *addr = nordic_storage->nordic.sdfw; + *size = sizeof(nordic_storage->nordic.sdfw); + break; + case SUIT_MANIFEST_SEC_SYSCTRL: + *addr = nordic_storage->nordic.scfw; + *size = sizeof(nordic_storage->nordic.scfw); + break; + case SUIT_MANIFEST_NORDIC_CELLFW: + *addr = nordic_storage_cell->nordic_cell.cellfw; + *size = sizeof(nordic_storage_cell->nordic_cell.top); + break; + case SUIT_MANIFEST_RAD_RECOVERY: + *addr = rad_storage->rad.recovery; + *size = sizeof(rad_storage->rad.recovery); + break; + case SUIT_MANIFEST_RAD_LOCAL_1: + *addr = rad_storage->rad.local_a; + *size = sizeof(rad_storage->rad.local_a); + break; + case SUIT_MANIFEST_RAD_LOCAL_2: + *addr = rad_storage->rad.local_b; + *size = sizeof(rad_storage->rad.local_b); + break; + case SUIT_MANIFEST_APP_ROOT: + *addr = app_storage->app.root; + *size = sizeof(app_storage->app.root); + break; + case SUIT_MANIFEST_APP_RECOVERY: + *addr = app_storage->app.recovery; + *size = sizeof(app_storage->app.recovery); + break; + case SUIT_MANIFEST_APP_LOCAL_1: + *addr = app_storage->app.local_a; + *size = sizeof(app_storage->app.local_a); + break; + case SUIT_MANIFEST_APP_LOCAL_2: + *addr = app_storage->app.local_b; + *size = sizeof(app_storage->app.local_b); + break; + case SUIT_MANIFEST_APP_LOCAL_3: + *addr = app_storage->app.local_spare; + *size = sizeof(app_storage->app.local_spare); + break; + default: + return SUIT_PLAT_ERR_OUT_OF_BOUNDS; + } + + return SUIT_PLAT_SUCCESS; +} + +/** @brief Verify digest of the area. + * + * @param[in] addr Address of the area to hash. + * @param[in] size Size of the area to hash. + * @param[in] digest Pointer to the structure with the digest value. + * + * @retval SUIT_PLAT_SUCCESS if the digest was successfully verified. + * @retval SUIT_PLAT_ERR_INVAL if one of the input arguments is not correct (i.e. NULL). + * @retval SUIT_PLAT_ERR_AUTHENTICATION if the digest value does not match with the area contents. + * @retval SUIT_PLAT_ERR_CRASH if failed to calculate digest value. + */ +static suit_plat_err_t sha256_check(const uint8_t *addr, size_t size, + const suit_storage_digest_t *exp_digest) +{ + suit_plat_err_t err = SUIT_PLAT_ERR_AUTHENTICATION; +#ifdef CONFIG_SUIT_CRYPTO + const psa_algorithm_t psa_alg = PSA_ALG_SHA_256; + const size_t exp_digest_length = PSA_HASH_LENGTH(psa_alg); + psa_hash_operation_t operation = {0}; + + if ((addr == NULL) || (exp_digest == NULL)) { + LOG_ERR("Invalid argument"); + return SUIT_PLAT_ERR_INVAL; + } + + if (exp_digest_length != sizeof(*exp_digest)) { + LOG_ERR("Invalid digest algorithm"); + return SUIT_PLAT_ERR_INVAL; + } + + psa_status_t status = psa_crypto_init(); + + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to init psa crypto: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_setup(&operation, psa_alg); + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to setup hash algorithm: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_update(&operation, addr, size); + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to calculate hash value: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_verify(&operation, *exp_digest, exp_digest_length); + if (status == PSA_SUCCESS) { + /* Digest calculation successful; expected digest matches calculated one */ + err = SUIT_PLAT_SUCCESS; + } else { + if (status == PSA_ERROR_INVALID_SIGNATURE) { + /* Digest calculation successful but expected digest does not match + * calculated one + */ + err = SUIT_PLAT_ERR_AUTHENTICATION; + } else { + LOG_ERR("psa_hash_verify error: %d", status); + err = SUIT_PLAT_ERR_CRASH; + } + /* In both cases psa_hash_verify enters error state and must be aborted */ + status = psa_hash_abort(&operation); + if (status != PSA_SUCCESS) { + LOG_ERR("psa_hash_abort error %d", status); + if (err == SUIT_PLAT_SUCCESS) { + err = SUIT_PLAT_ERR_CRASH; + } + } + } +#endif /* CONFIG_SUIT_CRYPTO */ + + return err; +} + +/** @brief Calculate digest of the area. + * + * @param[in] addr Address of the area to hash. + * @param[in] size Size of the area to hash. + * @param[out] digest Pointer to the output structure to store the digest value. + * + * @retval SUIT_PLAT_SUCCESS if the digest was successfully calculated. + * @retval SUIT_PLAT_ERR_INVAL if one of the input arguments is not correct (i.e. NULL). + * @retval SUIT_PLAT_ERR_CRASH if failed to calculate digest value. + */ +static suit_plat_err_t sha256_get(const uint8_t *addr, size_t size, suit_storage_digest_t *digest) +{ + suit_plat_err_t err = SUIT_PLAT_ERR_AUTHENTICATION; +#ifdef CONFIG_SUIT_CRYPTO + const psa_algorithm_t psa_alg = PSA_ALG_SHA_256; + size_t digest_length = PSA_HASH_LENGTH(psa_alg); + psa_hash_operation_t operation = {0}; + + if ((addr == NULL) || (digest == NULL)) { + LOG_ERR("Invalid argument"); + return SUIT_PLAT_ERR_INVAL; + } + + if (digest_length != sizeof(*digest)) { + LOG_ERR("Invalid digest algorithm"); + return SUIT_PLAT_ERR_INVAL; + } + + psa_status_t status = psa_crypto_init(); + + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to init psa crypto: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_setup(&operation, psa_alg); + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to setup hash algorithm: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_update(&operation, addr, size); + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to calculate hash value: %d", status); + return SUIT_PLAT_ERR_CRASH; + } + + status = psa_hash_finish(&operation, *digest, digest_length, &digest_length); + if (status == PSA_SUCCESS) { + /* Digest calculation successful; expected digest matches calculated one */ + err = SUIT_PLAT_SUCCESS; + } else { + LOG_ERR("psa_hash_finish error: %d", status); + err = SUIT_PLAT_ERR_CRASH; + + /* psa_hash_finish enters error state and must be aborted */ + status = psa_hash_abort(&operation); + if (status != PSA_SUCCESS) { + LOG_ERR("psa_hash_abort error %d", status); + } + } +#endif /* CONFIG_SUIT_CRYPTO */ + + return err; +} + +/** @brief Helper function to override destination area with source contents. + * + * @param[in] dst_addr Address of the destination area. + * @param[in] src_addr Address of the source area. + * @param[in] size Size of the area. + * + * @retval SUIT_PLAT_SUCCESS if area is successfully copied. + * @retval SUIT_PLAT_ERR_HW_NOT_READY if the flash controller device is not available. + * @retval SUIT_PLAT_ERR_IO if unable to modify the destination area. + */ +static suit_plat_err_t flash_cpy(uint8_t *dst_addr, uint8_t *src_addr, size_t size) +{ + const struct device *fdev = SUIT_PLAT_INTERNAL_NVM_DEV; + + if (!device_is_ready(fdev)) { + return SUIT_PLAT_ERR_HW_NOT_READY; + } + + int err = flash_erase(fdev, suit_plat_mem_nvm_offset_get(dst_addr), size); + + if (err == 0) { + err = flash_write(fdev, suit_plat_mem_nvm_offset_get(dst_addr), src_addr, size); + } + + if (err == 0) { + return SUIT_PLAT_SUCCESS; + } else { + return SUIT_PLAT_ERR_IO; + } +} + +/** @brief Select the digest-protected area with valid sigest. + * + * @param[in,out] area_addr Address of the area. + * @param[in,out] backup_addr Address of the backup. + * @param[in] area_size Size of the area. + * + * @retval NULL if area and backup are invalid. + * @retval area_addr if area is valid. + * @retval backup_addr if area is invalid, but backup is valid. + */ +static uint8_t *digest_struct_find(uint8_t *area_addr, uint8_t *backup_addr, size_t area_size) +{ + const suit_storage_digest_t *area_digest = NULL; + const suit_storage_digest_t *backup_digest = NULL; + suit_plat_err_t area_status = SUIT_PLAT_ERR_CRASH; + suit_plat_err_t backup_status = SUIT_PLAT_ERR_CRASH; + + if ((area_addr == NULL) || (backup_addr == NULL) || (area_size == 0) || + (area_size != EB_ALIGN(area_size))) { + LOG_ERR("Invalid argument"); + return NULL; + } + + area_digest = (suit_storage_digest_t *)&area_addr[area_size]; + area_status = sha256_check(area_addr, area_size, area_digest); + + if (area_status == SUIT_PLAT_SUCCESS) { + return area_addr; + } + + backup_digest = (suit_storage_digest_t *)&backup_addr[area_size]; + backup_status = sha256_check(backup_addr, area_size, backup_digest); + + if (backup_status == SUIT_PLAT_SUCCESS) { + return backup_addr; + } + + return NULL; +} + +/** @brief Validate the integrity of the digest-protected area in NVM. + * + * @details This function calculates and compares digests of the area and the assigned backup. + * It is assumed, that the digest value of type @ref suit_storage_digest_t is stored right + * after the data. + * If the area are valid, the backup is updated. + * If the area is invalid, but a valid backup is found, the backup is copied into the area. + * If backup is invalid, but the area is valid, the area is copied into the backup. + * If both the area and the backup are invalid, this function will fail. + * + * @param[in,out] area_addr Address of the area. + * @param[in,out] backup_addr Address of the backup. + * @param[in] area_size Size of the area. + * + * @retval SUIT_PLAT_SUCCESS if area is successfully verified or fixed. + * @retval SUIT_PLAT_ERR_INVAL if one of the input arguments is not correct (i.e. NULL). + * @retval SUIT_PLAT_ERR_HW_NOT_READY if the flash controller device is not available. + * @retval SUIT_PLAT_ERR_IO if unable to modify the area or the backup using flash + * controller device. + * @retval SUIT_PLAT_ERR_CRASH if failed to calculate digest value. + * @retval SUIT_PLAT_ERR_AUTHENTICATION if both the area and the backup are invalid. + */ +static suit_plat_err_t digest_struct_validate(uint8_t *area_addr, uint8_t *backup_addr, + size_t area_size) +{ + const size_t area_with_digest_size = area_size + EB_SIZE(suit_storage_digest_t); + const suit_storage_digest_t *area_digest = NULL; + const suit_storage_digest_t *backup_digest = NULL; + suit_plat_err_t area_status = SUIT_PLAT_ERR_CRASH; + suit_plat_err_t backup_status = SUIT_PLAT_ERR_CRASH; + + if ((area_addr == NULL) || (backup_addr == NULL) || (area_size == 0) || + (area_size != EB_ALIGN(area_size))) { + LOG_ERR("Invalid argument"); + return SUIT_PLAT_ERR_INVAL; + } + + area_digest = (suit_storage_digest_t *)&area_addr[area_size]; + backup_digest = (suit_storage_digest_t *)&backup_addr[area_size]; + + area_status = sha256_check(area_addr, area_size, area_digest); + backup_status = sha256_check(backup_addr, area_size, backup_digest); + + if (area_status == SUIT_PLAT_SUCCESS) { + if (backup_status == SUIT_PLAT_SUCCESS) { + if (memcmp(area_digest, backup_digest, sizeof(suit_storage_digest_t)) == + 0) { + return SUIT_PLAT_SUCCESS; + } + } + + /* We have a valid entry and backup digest does not match or differ. */ + LOG_INF("Backup area 0x%lx -> 0x%lx", (uintptr_t)area_addr, (uintptr_t)backup_addr); + + return flash_cpy(backup_addr, area_addr, area_with_digest_size); + } else if (backup_status == SUIT_PLAT_SUCCESS) { + /* Regular entry broken - restore from backup. */ + LOG_INF("Use backup 0x%lx -> 0x%lx", (uintptr_t)backup_addr, (uintptr_t)area_addr); + + return flash_cpy(area_addr, backup_addr, area_with_digest_size); + } + + /* Both regular and backup entries broken - fail. */ + return SUIT_PLAT_ERR_AUTHENTICATION; +} + +/** @brief Update the area digest value and create a new backup of the digest-protected area in NVM. + * + * @param[in,out] area_addr Address of the area. + * @param[in,out] backup_addr Address of the backup. + * @param[in] area_size Size of the area. + * + * @retval SUIT_PLAT_SUCCESS if area is successfully updated. + * @retval SUIT_PLAT_ERR_INVAL if one of the input arguments is not correct (i.e. NULL). + * @retval SUIT_PLAT_ERR_HW_NOT_READY if the flash controller device is not available. + * @retval SUIT_PLAT_ERR_IO if unable to modify the area or the backup using flash + * controller device. + * @retval SUIT_PLAT_ERR_CRASH if both the area and the backup are invalid. + */ +static suit_plat_err_t digest_struct_commit(uint8_t *area_addr, uint8_t *backup_addr, + size_t area_size) +{ + const struct device *fdev = SUIT_PLAT_INTERNAL_NVM_DEV; + const suit_storage_digest_t *area_digest = NULL; + suit_storage_digest_t digest; + + if ((area_addr == NULL) || (backup_addr == NULL) || (area_size == 0) || + (area_size != EB_ALIGN(area_size))) { + LOG_ERR("Invalid argument"); + return SUIT_PLAT_ERR_INVAL; + } + + area_digest = (suit_storage_digest_t *)&area_addr[area_size]; + + if (!device_is_ready(fdev)) { + fdev = NULL; + return SUIT_PLAT_ERR_HW_NOT_READY; + } + + suit_plat_err_t ret = sha256_get(area_addr, area_size, &digest); + + if (ret != SUIT_PLAT_SUCCESS) { + return ret; + } + + /* Override regular entry. */ + int err = flash_erase(fdev, suit_plat_mem_nvm_offset_get((uint8_t *)(*area_digest)), + EB_SIZE(suit_storage_digest_t)); + if (err != 0) { + return SUIT_PLAT_ERR_IO; + } + + err = flash_write(fdev, suit_plat_mem_nvm_offset_get((uint8_t *)(*area_digest)), digest, + sizeof(digest)); + if (err != 0) { + return SUIT_PLAT_ERR_IO; + } + + /* Verify the new area value and backup if digest is correct. */ + err = digest_struct_validate(area_addr, backup_addr, area_size); + if (err == SUIT_PLAT_ERR_AUTHENTICATION) { + err = SUIT_PLAT_ERR_CRASH; + } + + return err; +} + +/** @brief Initialize NVV area with default values. + * + * @details Ideally, the SUIT storage NVV module should place the default values (0xFF) and this + * function should append the correct digest at the end of it. + * + * @param[out] area_addr Address of the NVV area to initialize. + * @param[out] backup_addr Address of the NVV backup area to initialize. + * + * @retval SUIT_PLAT_SUCCESS if area is successfully initialized. + * @retval SUIT_PLAT_ERR_INVAL if one of the input arguments is not correct (i.e. NULL). + * @retval SUIT_PLAT_ERR_HW_NOT_READY if the flash controller device is not available. + * @retval SUIT_PLAT_ERR_CRASH if failed to calculate digest value. + * @retval SUIT_PLAT_ERR_IO if unable to modify the area using flash controller device. + */ +static suit_plat_err_t nvv_init(uint8_t *area_addr, uint8_t *backup_addr) +{ + size_t area_size = offsetof(struct suit_storage_nvv, digest); + suit_plat_err_t ret = suit_storage_nvv_erase(area_addr, area_size); + + if (ret != SUIT_PLAT_SUCCESS) { + return ret; + } + + /* Update digest and backup. */ + return digest_struct_commit(area_addr, backup_addr, area_size); +} + +static suit_plat_err_t find_mpi_area(suit_manifest_role_t role, uint8_t **addr, size_t *size) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + struct suit_storage_rad *rad_storage = (struct suit_storage_rad *)SUIT_STORAGE_RAD_ADDRESS; + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + struct suit_storage_mpi_app *app_mpi = (struct suit_storage_mpi_app *)digest_struct_find( + (uint8_t *)&app_storage->app.mpi, (uint8_t *)&nordic_storage->nordic.app_mpi_bak, + offsetof(struct suit_storage_mpi_app, digest)); + struct suit_storage_mpi_rad *rad_mpi = (struct suit_storage_mpi_rad *)digest_struct_find( + (uint8_t *)&rad_storage->rad.mpi, (uint8_t *)&nordic_storage->nordic.rad_mpi_bak, + offsetof(struct suit_storage_mpi_rad, digest)); + + switch (role) { + case SUIT_MANIFEST_SEC_TOP: + *addr = (uint8_t *)&mpi_nordic[0]; + *size = sizeof(mpi_nordic[0]); + break; + case SUIT_MANIFEST_SEC_SDFW: + *addr = (uint8_t *)&mpi_nordic[1]; + *size = sizeof(mpi_nordic[1]); + break; + case SUIT_MANIFEST_SEC_SYSCTRL: + *addr = (uint8_t *)&mpi_nordic[2]; + *size = sizeof(mpi_nordic[2]); + break; + case SUIT_MANIFEST_RAD_RECOVERY: + if (rad_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = rad_mpi->recovery; + *size = sizeof(rad_mpi->recovery); + break; + case SUIT_MANIFEST_RAD_LOCAL_1: + if (rad_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = rad_mpi->local_a; + *size = sizeof(rad_mpi->local_a); + break; + case SUIT_MANIFEST_RAD_LOCAL_2: + if (rad_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = rad_mpi->local_b; + *size = sizeof(rad_mpi->local_b); + break; + case SUIT_MANIFEST_APP_ROOT: + if (app_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = app_mpi->root; + *size = sizeof(app_mpi->root); + break; + case SUIT_MANIFEST_APP_RECOVERY: + if (app_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = app_mpi->recovery; + *size = sizeof(app_mpi->recovery); + break; + case SUIT_MANIFEST_APP_LOCAL_1: + if (app_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = app_mpi->local_a; + *size = sizeof(app_mpi->local_a); + break; + case SUIT_MANIFEST_APP_LOCAL_2: + if (app_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = app_mpi->local_b; + *size = sizeof(app_mpi->local_b); + break; + case SUIT_MANIFEST_APP_LOCAL_3: + if (app_mpi == NULL) { + return SUIT_PLAT_ERR_NOT_FOUND; + } + *addr = app_mpi->local_spare; + *size = sizeof(app_mpi->local_spare); + break; + default: + return SUIT_PLAT_ERR_OUT_OF_BOUNDS; + } + + return SUIT_PLAT_SUCCESS; +} + +/** @brief Iterate through roles and try to initialize MPI areas. + */ +static suit_plat_err_t configure_manifests(suit_manifest_role_t *roles, size_t num_roles, + bool ignore_missing) +{ + suit_plat_err_t ret = SUIT_PLAT_SUCCESS; + uint8_t *mpi_addr; + size_t mpi_size; + + for (size_t i = 0; i < num_roles; i++) { + ret = find_mpi_area(roles[i], &mpi_addr, &mpi_size); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to locate MPI area for role 0x%x%s: %d", roles[i], + suit_role_name_get(roles[i]), ret); + return ret; + } + + ret = suit_storage_mpi_configuration_load(roles[i], mpi_addr, mpi_size); + if (ret != SUIT_PLAT_SUCCESS) { + if ((ret == SUIT_PLAT_ERR_NOT_FOUND) && (ignore_missing)) { + LOG_INF("Skip MPI area for role 0x%x%s. Area load failed.", + roles[i], suit_role_name_get(roles[i])); + continue; + } else { + LOG_WRN("Failed to load MPI configuration for role 0x%x%s: %d", + roles[i], suit_role_name_get(roles[i]), ret); + return ret; + } + } + } + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t find_report_area(size_t index, uint8_t **addr, size_t *size) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + + switch (index) { + case 0: + *addr = nordic_storage->nordic.report_0; + *size = ARRAY_SIZE(nordic_storage->nordic.report_0); + break; + default: + return SUIT_PLAT_ERR_OUT_OF_BOUNDS; + } + + return SUIT_PLAT_SUCCESS; +} + +suit_plat_err_t suit_storage_init(void) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + struct suit_storage_rad *rad_storage = (struct suit_storage_rad *)SUIT_STORAGE_RAD_ADDRESS; + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + suit_plat_err_t ret = SUIT_PLAT_SUCCESS; + suit_manifest_role_t nordic_roles[] = { + SUIT_MANIFEST_SEC_TOP, + SUIT_MANIFEST_SEC_SDFW, + SUIT_MANIFEST_SEC_SYSCTRL, + }; + + if ((sizeof(struct suit_storage_nordic) > SUIT_STORAGE_NORDIC_SIZE) || + (sizeof(struct suit_storage_rad) > SUIT_STORAGE_RAD_SIZE) || + (sizeof(struct suit_storage_app) > SUIT_STORAGE_APP_SIZE)) { + return SUIT_PLAT_ERR_NOMEM; + } + + if ((EB_ALIGN(SUIT_STORAGE_NORDIC_SIZE) != SUIT_STORAGE_NORDIC_SIZE) || + (EB_ALIGN(SUIT_STORAGE_RAD_SIZE) != SUIT_STORAGE_RAD_SIZE) || + (EB_ALIGN(SUIT_STORAGE_APP_SIZE) != SUIT_STORAGE_APP_SIZE)) { + return SUIT_PLAT_ERR_CRASH; + } + + ret = suit_storage_mpi_init(); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize MPI submodule: %d", ret); + return ret; + } + + ret = suit_storage_report_internal_init(); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize report submodule: %d", ret); + return ret; + } + + ret = suit_storage_flags_internal_init(); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize flags submodule: %d", ret); + return ret; + } + + ret = suit_storage_nvv_init(); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to init NVV: %d", ret); + return ret; + } + + ret = digest_struct_validate((uint8_t *)&app_storage->app.nvv, + (uint8_t *)&app_storage->app.nvv_bak, + offsetof(struct suit_storage_nvv, digest)); + if (ret == SUIT_PLAT_ERR_AUTHENTICATION) { + LOG_WRN("Failed to verify NVV (%d), load default values", ret); + ret = nvv_init((uint8_t *)&app_storage->app.nvv, + (uint8_t *)&app_storage->app.nvv_bak); + } + + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize NVV with default values: %d", ret); + return ret; + } + + /* Bootstrap Nordic MPI entries. */ + ret = configure_manifests(nordic_roles, ARRAY_SIZE(nordic_roles), false); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize Nordic MPIs: %d", ret); + return ret; + } + + ret = digest_struct_validate((uint8_t *)&rad_storage->rad.mpi, + (uint8_t *)&nordic_storage->nordic.rad_mpi_bak, + offsetof(struct suit_storage_mpi_rad, digest)); + if (ret == SUIT_PLAT_SUCCESS) { + suit_manifest_role_t roles[] = { + SUIT_MANIFEST_RAD_RECOVERY, + SUIT_MANIFEST_RAD_LOCAL_1, + SUIT_MANIFEST_RAD_LOCAL_2, + }; + + ret = configure_manifests(roles, ARRAY_SIZE(roles), true); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize Radio MPIs: %d", ret); + return ret; + } + } else { + LOG_WRN("Failed to verify radio MPI: %d", ret); + } + + ret = digest_struct_validate((uint8_t *)&app_storage->app.mpi, + (uint8_t *)&nordic_storage->nordic.app_mpi_bak, + offsetof(struct suit_storage_mpi_app, digest)); + if (ret == SUIT_PLAT_SUCCESS) { + suit_manifest_role_t essential_roles[] = { + SUIT_MANIFEST_APP_ROOT, + }; + suit_manifest_role_t optional_roles[] = { + SUIT_MANIFEST_APP_RECOVERY, + SUIT_MANIFEST_APP_LOCAL_1, + SUIT_MANIFEST_APP_LOCAL_2, + SUIT_MANIFEST_APP_LOCAL_3, + }; + + ret = configure_manifests(essential_roles, ARRAY_SIZE(essential_roles), false); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize essential Application MPIs: %d", ret); + return ret; + } + + ret = configure_manifests(optional_roles, ARRAY_SIZE(optional_roles), true); + if (ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to initialize non-essential Application MPIs: %d", ret); + return ret; + } + } else { + LOG_ERR("Failed to verify application MPI: %d", ret); + /* Lack of application MPI means lack of ROOT as well as recovery class IDs. + * In such case, the system is unable to boot anything except manifests controlled + * by Nordic. + */ + return ret; + } + + return SUIT_PLAT_SUCCESS; +} + +suit_plat_err_t suit_storage_update_cand_get(const suit_plat_mreg_t **regions, size_t *len) +{ + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + uint8_t *addr = app_storage->app.update_cand; + size_t size = sizeof(app_storage->app.update_cand); + + return suit_storage_update_get(addr, size, regions, len); +} + +suit_plat_err_t suit_storage_update_cand_set(suit_plat_mreg_t *regions, size_t len) +{ + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + uint8_t *addr = app_storage->app.update_cand; + size_t size = sizeof(app_storage->app.update_cand); + + return suit_storage_update_set(addr, size, regions, len); +} + +suit_plat_err_t suit_storage_installed_envelope_get(const suit_manifest_class_id_t *id, + const uint8_t **addr, size_t *size) +{ + suit_manifest_role_t role; + + if ((id == NULL) || (addr == NULL) || (size == 0)) { + return SUIT_PLAT_ERR_INVAL; + } + + suit_plat_err_t err = suit_storage_mpi_role_get(id, &role); + + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to find role for given class ID."); + return err; + } + + err = find_manifest_area(role, addr, size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_ERR("Unable to find area for envelope with role 0x%x%s.", role, + suit_role_name_get(role)); + return err; + } + + LOG_DBG("Decode envelope with role: 0x%x%s address: 0x%lx", role, suit_role_name_get(role), + (intptr_t)(*addr)); + + err = suit_storage_envelope_get(*addr, *size, id, addr, size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to parse envelope with role 0x%x%s", role, + suit_role_name_get(role)); + return err; + } + + LOG_DBG("Valid envelope with given class ID and role 0x%x%s found", role, + suit_role_name_get(role)); + + return err; +} + +suit_plat_err_t suit_storage_install_envelope(const suit_manifest_class_id_t *id, uint8_t *addr, + size_t size) +{ + suit_plat_err_t err; + suit_manifest_role_t role; + uint8_t *area_addr = NULL; + size_t area_size = 0; + + err = suit_storage_mpi_role_get(id, &role); + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to find role for given class ID."); + return err; + } + + if ((addr == NULL) || (size == 0)) { + return SUIT_PLAT_ERR_INVAL; + } + + err = find_manifest_area(role, (const uint8_t **)&area_addr, &area_size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_ERR("Unable to find area for envelope with role 0x%x%s.", role, + suit_role_name_get(role)); + return err; + } + + err = suit_storage_envelope_install(area_addr, area_size, id, addr, size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to install envelope with role 0x%x%s.", role, + suit_role_name_get(role)); + return err; + } + + LOG_INF("Envelope with role 0x%x%s saved.", role, suit_role_name_get(role)); + + return err; +} + +suit_plat_err_t suit_storage_var_get(size_t index, uint8_t *value) +{ + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + size_t area_size = offsetof(struct suit_storage_nvv, digest); + uint8_t *area_addr = (uint8_t *)digest_struct_find( + (uint8_t *)&app_storage->app.nvv, (uint8_t *)&app_storage->app.nvv_bak, + offsetof(struct suit_storage_nvv, digest)); + + return suit_storage_nvv_get(area_addr, area_size, index, value); +} + +suit_plat_err_t suit_storage_var_set(size_t index, uint8_t value) +{ + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + size_t area_size = offsetof(struct suit_storage_nvv, digest); + uint8_t *backup_addr = (uint8_t *)&app_storage->app.nvv_bak; + uint8_t *area_addr = + (uint8_t *)digest_struct_find((uint8_t *)&app_storage->app.nvv, backup_addr, + offsetof(struct suit_storage_nvv, digest)); + + /* It is not allowed to update NVVs if the area has incorrect digest. */ + if (area_addr == backup_addr) { + return SUIT_PLAT_ERR_IO; + } + + suit_plat_err_t err = suit_storage_nvv_set(area_addr, area_size, index, value); + + if (err != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to update NVV at index %d", index); + return err; + } + + /* Update digest and backup. */ + return digest_struct_commit(area_addr, backup_addr, area_size); +} + +suit_plat_err_t suit_storage_report_clear(size_t index) +{ + uint8_t *area_addr = NULL; + size_t area_size = 0; + suit_plat_err_t err; + + err = find_report_area(index, &area_addr, &area_size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to find report area at index %d.", index); + return err; + } + + return suit_storage_report_internal_clear(area_addr, area_size); +} + +suit_plat_err_t suit_storage_report_save(size_t index, const uint8_t *buf, size_t len) +{ + uint8_t *area_addr = NULL; + size_t area_size = 0; + suit_plat_err_t err; + + err = find_report_area(index, &area_addr, &area_size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to find area for report at index %d.", index); + return err; + } + + return suit_storage_report_internal_save(area_addr, area_size, buf, len); +} + +suit_plat_err_t suit_storage_report_read(size_t index, const uint8_t **buf, size_t *len) +{ + uint8_t *area_addr = NULL; + size_t area_size = 0; + suit_plat_err_t err; + + err = find_report_area(index, &area_addr, &area_size); + if (err != SUIT_PLAT_SUCCESS) { + LOG_WRN("Unable to find area with report at index %d.", index); + return err; + } + + return suit_storage_report_internal_read(area_addr, area_size, buf, len); +} + +suit_plat_err_t suit_storage_purge(suit_manifest_domain_t domain) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + struct suit_storage_rad *rad_storage = (struct suit_storage_rad *)SUIT_STORAGE_RAD_ADDRESS; + struct suit_storage_app *app_storage = (struct suit_storage_app *)SUIT_STORAGE_APP_ADDRESS; + struct suit_storage_nordic_cell *cell_storage = + (struct suit_storage_nordic_cell *)SUIT_STORAGE_NORDIC_CELL_ADDRESS; + const struct device *fdev = SUIT_PLAT_INTERNAL_NVM_DEV; + suit_plat_err_t ret = SUIT_PLAT_SUCCESS; + int err = 0; + + if (!device_is_ready(fdev)) { + return SUIT_PLAT_ERR_HW_NOT_READY; + } + + switch (domain) { + case SUIT_MANIFEST_DOMAIN_APP: + /* Clear regular entry, inluding NVV and NVV backup. */ + err = flash_erase(fdev, + suit_plat_mem_nvm_offset_get((uint8_t *)&app_storage->app_area), + sizeof(app_storage->app_area)); + if (err == 0) { + /* Clear MPI backup. */ + err = flash_erase(fdev, + suit_plat_mem_nvm_offset_get( + (uint8_t *)&nordic_storage->nordic.app_mpi_bak), + sizeof(nordic_storage->nordic.app_mpi_bak)); + } + + if (err == 0) { + /* Clear boot flags. */ + err = flash_erase(fdev, + suit_plat_mem_nvm_offset_get( + (uint8_t *)&nordic_storage->nordic.flags), + sizeof(nordic_storage->nordic.flags)); + } + + /* Clear reports (incl. recovery flag and update candidate info). */ + ret = suit_storage_report_clear(0); + break; + + case SUIT_MANIFEST_DOMAIN_RAD: + /* Clear regular entry. */ + err = flash_erase(fdev, + suit_plat_mem_nvm_offset_get((uint8_t *)&rad_storage->rad_area), + sizeof(rad_storage->rad_area)); + if (err == 0) { + /* Clear MPI backup. */ + err = flash_erase(fdev, + suit_plat_mem_nvm_offset_get( + (uint8_t *)&nordic_storage->nordic.rad_mpi_bak), + sizeof(nordic_storage->nordic.rad_mpi_bak)); + } + break; + + case SUIT_MANIFEST_DOMAIN_NORDIC_CELL: + /* Clear regular entry. */ + err = flash_erase( + fdev, + suit_plat_mem_nvm_offset_get((uint8_t *)&cell_storage->nordic_cell.cellfw), + sizeof(cell_storage->nordic_cell.cellfw)); + break; + + default: + return SUIT_PLAT_ERR_INVAL; + } + + /* Reinitialize SUIT storage internal structures. + * Ignore return code as an init failure on erased SUIT storage area + * does not indicate that the purge failed. + */ + (void)suit_storage_init(); + + /* In case of IO error, ignore the suit processor return code. */ + if (err != 0) { + return SUIT_PLAT_ERR_IO; + } + + return ret; +} + +suit_plat_err_t suit_storage_flags_clear(suit_storage_flag_t flag) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + uint8_t *area_addr = nordic_storage->nordic.flags.flags; + size_t area_size = ARRAY_SIZE(nordic_storage->nordic.flags.flags); + + return suit_storage_flags_internal_clear(area_addr, area_size, flag); +} + +suit_plat_err_t suit_storage_flags_set(suit_storage_flag_t flag) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + uint8_t *area_addr = nordic_storage->nordic.flags.flags; + size_t area_size = ARRAY_SIZE(nordic_storage->nordic.flags.flags); + + return suit_storage_flags_internal_set(area_addr, area_size, flag); +} + +suit_plat_err_t suit_storage_flags_check(suit_storage_flag_t flag) +{ + struct suit_storage_nordic *nordic_storage = + (struct suit_storage_nordic *)SUIT_STORAGE_NORDIC_ADDRESS; + uint8_t *area_addr = nordic_storage->nordic.flags.flags; + size_t area_size = ARRAY_SIZE(nordic_storage->nordic.flags.flags); + + return suit_storage_flags_internal_check(area_addr, area_size, flag); +} diff --git a/subsys/suit/stream/Kconfig b/subsys/suit/stream/Kconfig index 50bf9b00a5f7..6e3f04f0924b 100644 --- a/subsys/suit/stream/Kconfig +++ b/subsys/suit/stream/Kconfig @@ -42,7 +42,7 @@ config SUIT_STREAM_SINK_EXTMEM menuconfig SUIT_STREAM_SINK_SDFW bool "Enable SDFW sink" depends on SUIT_UTILS - depends on SOC_SERIES_NRF54HX + depends on SOC_SERIES_NRF54HX || SOC_SERIES_NRF92X select PSA_WANT_ALG_SHA_512 select SUIT_STREAM_SINK_DIGEST @@ -80,7 +80,7 @@ endif # SUIT_STREAM_SINK_SDFW menuconfig SUIT_STREAM_SINK_SDFW_RECOVERY bool "Enable SDFW Recovery sink" depends on SUIT_UTILS - depends on SOC_SERIES_NRF54HX + depends on SOC_SERIES_NRF54HX || SOC_SERIES_NRF92X select PSA_WANT_ALG_SHA_512 select SUIT_STREAM_SINK_DIGEST diff --git a/subsys/suit/validator/CMakeLists.txt b/subsys/suit/validator/CMakeLists.txt index ad1ea4da498d..beeffd1f7612 100644 --- a/subsys/suit/validator/CMakeLists.txt +++ b/subsys/suit/validator/CMakeLists.txt @@ -11,6 +11,7 @@ target_link_libraries(suit_validator INTERFACE suit_platform_err) zephyr_library() zephyr_library_sources_ifdef(CONFIG_SUIT_VALIDATOR_IMPL_NRF54H20_SDFW src/suit_validator_nrf54h20.c) +zephyr_library_sources_ifdef(CONFIG_SUIT_VALIDATOR_IMPL_NRF9280_SDFW src/suit_validator_nrf9280.c) zephyr_library_link_libraries(suit_validator) zephyr_library_link_libraries(suit_memory_layout_interface) diff --git a/subsys/suit/validator/Kconfig b/subsys/suit/validator/Kconfig index 8ff7d72be768..2acd743cb456 100644 --- a/subsys/suit/validator/Kconfig +++ b/subsys/suit/validator/Kconfig @@ -18,6 +18,11 @@ config SUIT_VALIDATOR_IMPL_NRF54H20_SDFW depends on SOC_SERIES_NRF54HX depends on SUIT_PLATFORM_VARIANT_SDFW +config SUIT_VALIDATOR_IMPL_NRF9280_SDFW + bool "nRF9280: Secure domain" + depends on SOC_SERIES_NRF92X + depends on SUIT_PLATFORM_VARIANT_SDFW + config SUIT_VALIDATOR_IMPL_CUSTOM bool "custom" diff --git a/subsys/suit/validator/src/suit_validator_nrf9280.c b/subsys/suit/validator/src/suit_validator_nrf9280.c new file mode 100644 index 000000000000..8a33bfa6bd1c --- /dev/null +++ b/subsys/suit/validator/src/suit_validator_nrf9280.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +static suit_plat_err_t validate_candidate_location_common(const uint8_t *address, size_t size) +{ + struct arbiter_mem_params_access mem_params = { + .allowed_types = ARBITER_MEM_TYPE(RESERVED, FIXED, FREE), + .access = { + .owner = NRF_OWNER_APPLICATION, + .permissions = ARBITER_MEM_PERM(READ, SECURE), + .address = (uintptr_t)address, + .size = size, + }, + }; + + if (arbiter_mem_access_check(&mem_params) == ARBITER_STATUS_OK) { + return SUIT_PLAT_SUCCESS; + } + + mem_params.access.owner = NRF_OWNER_RADIOCORE; + + if (arbiter_mem_access_check(&mem_params) == ARBITER_STATUS_OK) { + return SUIT_PLAT_SUCCESS; + } + + return SUIT_PLAT_ERR_ACCESS; +} + +suit_plat_err_t suit_validator_validate_update_candidate_location(const uint8_t *address, + size_t size) +{ + return validate_candidate_location_common(address, size); +} + +suit_plat_err_t suit_validator_validate_cache_pool_location(const uint8_t *address, size_t size) +{ + if (suit_memory_global_address_range_is_in_external_memory((uintptr_t)address, size)) { + return SUIT_PLAT_SUCCESS; + } + + return validate_candidate_location_common(address, size); +} diff --git a/sysbuild/Kconfig.suit_provisioning b/sysbuild/Kconfig.suit_provisioning index b9967a6c1c68..2f02969bce0d 100644 --- a/sysbuild/Kconfig.suit_provisioning +++ b/sysbuild/Kconfig.suit_provisioning @@ -8,7 +8,7 @@ menu "SUIT provisioning" config SUIT_MPI_GENERATE bool "Generate SUIT Manifest Provisioning Information binary" - default y if SOC_SERIES_NRF54HX + default y if SOC_SERIES_NRF54HX || SOC_SERIES_NRF92X if SUIT_MPI_GENERATE @@ -17,6 +17,7 @@ config SUIT_ENVELOPE_OUTPUT_MPI_MERGE default y rsource "suit_provisioning/Kconfig.nrf54h20" +rsource "suit_provisioning/Kconfig.nrf9280" endif # SUIT_MPI_GENERATE diff --git a/sysbuild/suit_provisioning/Kconfig.nrf9280 b/sysbuild/suit_provisioning/Kconfig.nrf9280 new file mode 100644 index 000000000000..62550adc7e91 --- /dev/null +++ b/sysbuild/suit_provisioning/Kconfig.nrf9280 @@ -0,0 +1,167 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# +# WARNING: +# All addresses and sizes in this file must be sunchronized with the SUIT storage address +# defined in the Device Tree. +# Any modification of those values require alignment in the SDFW. +# Contents of the MPI (vendor name, class name, policies) may be modified by the +# product vendor. +# + +config SUIT_MPI_SOC_NRF9280 + bool + default y if SOC_SERIES_NRF92X + +if SUIT_MPI_SOC_NRF9280 + +manifest=ROOT +manifest-class=nRF9280_sample_root +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=y +manifest-signature-check=DISABLED +manifest-mpi-offset=0x3000 + +menu "Root manifest configuration" + +# Define as a regular config, so it is impossible to disable generation of the +# ROOT manifest configuration. +config SUIT_MPI_$(manifest) + bool + default y + +rsource "Kconfig.template.manifest_config" + +endmenu + +manifest=APP_RECOVERY +manifest-class=nRF9280_app_recovery +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=y +manifest-signature-check=DISABLED +manifest-mpi-offset=0x3030 + +menuconfig SUIT_MPI_$(manifest) + bool "Application recovery manifest configuration" + +rsource "Kconfig.template.manifest_config" + +manifest=APP_LOCAL_1 +manifest-class=nRF9280_sample_app +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x3060 + +menuconfig SUIT_MPI_$(manifest) + bool "Application local (1) manifest configuration" + default y + +rsource "Kconfig.template.manifest_config" + +manifest=APP_LOCAL_2 +manifest-class=nRF9280_sample_app_local_2 +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x3090 + +menuconfig SUIT_MPI_$(manifest) + bool "Application local (2) manifest configuration" + +rsource "Kconfig.template.manifest_config" + +manifest=APP_LOCAL_3 +manifest-class=nRF9280_sample_app_local_3 +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x30c0 + +menuconfig SUIT_MPI_$(manifest) + bool "Application local (3) manifest configuration" + +rsource "Kconfig.template.manifest_config" + +config SUIT_MPI_APP_AREA_PATH + string "Path to the file with all application domain MPI configurations" + default "suit_mpi_application_merged.hex" + +config SUIT_MPI_APP_AREA_OFFSET + hex + default 0x3000 + help + Offset of the application domain MPI configurations + The offset is calulated from the beginnig of the SUIT storage + +config SUIT_MPI_APP_AREA_SIZE + hex + default 0x0f0 + help + Size of the application domain MPI configurations + +manifest=RAD_RECOVERY +manifest-class=nRF9280_rad_recovery +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x2000 + +menuconfig SUIT_MPI_$(manifest) + bool "Radio recovery manifest configuration" + +rsource "Kconfig.template.manifest_config" + +manifest=RAD_LOCAL_1 +manifest-class=nRF9280_sample_rad +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x2030 + +menuconfig SUIT_MPI_$(manifest) + bool "Radio local (1) manifest configuration" + default y + +rsource "Kconfig.template.manifest_config" + +manifest=RAD_LOCAL_2 +manifest-class=nRF9280_sample_rad_local_2 +manifest-downgrade-prevention-enabled=n +manifest-independent-updates=n +manifest-signature-check=DISABLED +manifest-mpi-offset=0x2060 + +menuconfig SUIT_MPI_$(manifest) + bool "Radio local (2) manifest configuration" + +rsource "Kconfig.template.manifest_config" + +config SUIT_MPI_RAD_AREA_PATH + string "Path to the file with all radio domain MPI configurations" + default "suit_mpi_radio_merged.hex" + +config SUIT_MPI_RAD_AREA_OFFSET + hex + default 0x2000 + help + Offset of the radio domain MPI configurations + The offset is calulated from the beginnig of the SUIT storage + +config SUIT_MPI_RAD_AREA_SIZE + hex + default 0x90 + help + Size of the radio domain MPI configurations + +config SUIT_MPI_SLOT_SIZE + hex + default 0x30 + help + Size of each MPI configuration slot + +endif # SUIT_MPI_SOC_NRF9280 diff --git a/sysbuild/suit_provisioning/nrf9280.cmake b/sysbuild/suit_provisioning/nrf9280.cmake new file mode 100644 index 000000000000..3ab5ec5c4024 --- /dev/null +++ b/sysbuild/suit_provisioning/nrf9280.cmake @@ -0,0 +1,33 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# List all manifest roles. +# The function internally checks if the enabling symbol is defined. +# If role is enabled, the function verifies if all required options are defined. + +generate_mpi_hex(ROOT) +generate_mpi_hex(APP_RECOVERY) +generate_mpi_hex(APP_LOCAL_1) +generate_mpi_hex(APP_LOCAL_2) +generate_mpi_hex(APP_LOCAL_3) +generate_mpi_area( + APP_AREA + ROOT + APP_RECOVERY + APP_LOCAL_1 + APP_LOCAL_2 + APP_LOCAL_3 +) + +generate_mpi_hex(RAD_RECOVERY) +generate_mpi_hex(RAD_LOCAL_1) +generate_mpi_hex(RAD_LOCAL_2) +generate_mpi_area( + RAD_AREA + RAD_RECOVERY + RAD_LOCAL_1 + RAD_LOCAL_2 +) diff --git a/west.yml b/west.yml index 60660e3c6960..daedd59e59c8 100644 --- a/west.yml +++ b/west.yml @@ -242,7 +242,7 @@ manifest: upstream-sha: c6eaeda5a1c1c5dbb24dce7e027340cb8893a77b compare-by-default: false - name: suit-generator - revision: 9bd5f35396bea61f6c8e6aaebc00635364522ce6 + revision: pull/151/head path: modules/lib/suit-generator - name: suit-processor revision: a499bcadceff8877da63a0a140c6a91ff2f87b25