One of the samples available in the nRF Connect SDK is the Secure Partition Manager, which is required to run applications in the nonsecure domain of the CPU. The sample uses the SPM library available in NCS.
This post shows you how you can add your own custom SPM to your application, instead of modifying the one already in NCS. It also shows how to define your own secure functions in SPM that can be called from the non-secure domain.
The template project, written by Sebastian Bøe, is attached at the bottom of this tutorial. You will need to be on the 1.4 tag of NCS.
First off, what are the secure and nonsecure domains and why do we have them?
The Arm Cortex-M33 inside the nRF9160 comes with Arm’s TrustZone technology, which reduces the risk of an attack by isolating the critical security firmware from the rest of the application. In practice, this means it’s possible to create two environments inside the CPU that can run isolated from each other: a secure and a nonsecure environment. An application running as secure will only have access to the resources configured as secure, and same with nonsecure, which is what makes it resilient to attacks.
To run a nonsecure application, one first needs a secure application to configure permissions and resources as nonsecure before boot-up. This is what the Secure Partition Manager sample does. For how to actually do this, we need to get into something called multi-image builds.
Multi-image builds are when the firmware that is programmed consists of not only one application, but several separate images, where one of the images (the parent image) requires one or more other images (the child images) to be present. In NCS, all nonsecure samples require the secure partition manager as a child image to be able to access the CPU resources that are required by the application.
The SPM library provides default secure services to the nonsecure application, which are functions implemented in the secure firmware, but that can be called from the nonsecure firmware. These functions can be found in ncs/nrf/include/secure_services.h.
Since the nonsecure image is the parent image, you will have to build the application with the nonsecure board (nrf9160_pca10090ns).
This is what the project should look like. Note that this project folder doesn't necessarily have to be in the NCS path.
| |---non_secure_app |---secure_module |---zephyr |---CMakeLists.txt |---module.yml |---spm
Copy the SPM sample from NCS and place it in project/spm_module/zephyr/ as you can see in the diagram above. The addition of the zephyr directory so the path becomes spm_module/zephyr/spm is a convention for zephyr modules.
In the same location as the spm folder, there also needs to be a CMakeLists.txt file and a module.yml file. The CMakeLists.txt file will make sure that if the correct configuration is enabled (CONFIG_SPM), the spm sample is added as a child image like this:
The module.yml file needs to be present in all zephyr modules and looks like this:
# kconfig: zephyr/Kconfig
If you want to define Kconfigs for this module, you can create the file 'Kconfig' in the same location as module.yml and CMakeLists.txt and include the last line in module.yml that's been commented out (kconfig: zephyr/Kconfig).
Now that you’ve got those two files in place, you can define your secure function in the main.c file of the SPM sample like this:
Then you also need to add the following configuration to the prj.conf file in the SPM application:
This is to indicate that the SPM application contains secure entry functions that may be called from the non-secure state.
You can now call this function in your non-secure application like this. Make sure you declare the function first:
printk("Hello World! %s\n", CONFIG_BOARD);
In the prj.conf file in the non-secure application you need to enable the following configurations like this:
SPM to ensure that all the configurations that are implicitly set by this config are included. SPM_BUILD_STRATEGY_SKIP_BUILD so that the application doesn't build the SPM module in NCS. ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS is to indicate that the non-secure firmware uses secure functions, and ARM_ENTRY_VENEERS_LIB_NAME to point to where the lib-file generated by our custom SPM module is located.
The CMakeLists.txt file in the non_secure application needs to have the following code, to be placed under the line cmake_minimum_required:
This will add the spm_module in this project as a module in Zephyr, see here.
When you build this application you will see two warnings from CMake
CMake Warning at C:/ncs/nrf/samples/CMakeLists.txt:30 (message): NOTE: SPM is not built from source, and the firmware use secure entry functions. However, the configured library file is not found.
Ensure that the configuration 'ARM_ENTRY_VENEERS_LIB_NAME' points to the .a file (default 'spm/libspmsecureentries.a') generated alongside the flashed or merged SPM hex file
CMake Warning at C:/ncs/nrf/cmake/partition_manager.cmake:42 (message): One or more child image is not configured to be built from source. However, there is no static configuration provided to the partition manager. Please provide a static configuration as described in the 'Scripts -> Partition Manager -> Static configuration' chapter in the documentation. Without this information, the build system is not able to place the image correctly in flash.
These warnings can be ignored and only appear because we have enabled CONFIG_SPM, but do not include the SPM app in NCS in the build.
No, it's not possible to define variables on the non-secure region and read them from the secure region.
What you can do instead is make the variables available in the secure region and then create…