2022-03-25 06:22 PM
Hi All,
I am almost finished integrating the SBSFU into my project. I can run the SBSFU and user app and update the processor, STM32L4R9VITx, successfully while no protections are enabled. When I enable firewall protection it boots into my application but fails on the first write to a special function register. The SBSFU restarts with a firewall reset detected and retries the application.
My application uses FreeRTOS and the freeRTOS SVC and PendSVC handlers.
Any ideas as to why I cant write to the SFR's?
SBSFU Linker files and pictures of the debugger instructions below.
mapping_sbsfu.ld
/* SE Code region protected by firewall */
VECTOR_SIZE = 0x200;
__ICFEDIT_SE_Code_region_ROM_start__ = 0x08000000 + VECTOR_SIZE;
__ICFEDIT_SE_CallGate_region_ROM_start__ = __ICFEDIT_SE_Code_region_ROM_start__ + 0x4;
__ICFEDIT_SE_CallGate_region_ROM_end__ = __ICFEDIT_SE_Code_region_ROM_start__ + 0x1FF;
/* SE key region protected by firewall */
__ICFEDIT_SE_Key_region_ROM_start__ = __ICFEDIT_SE_CallGate_region_ROM_end__ + 0x1;
__ICFEDIT_SE_Key_region_ROM_end__ = __ICFEDIT_SE_Key_region_ROM_start__ + 0xFF;
/* SE Startup: call before enabling firewall*/
__ICFEDIT_SE_Startup_region_ROM_start__ = __ICFEDIT_SE_Key_region_ROM_end__ + 0x1;
__ICFEDIT_SE_Code_nokey_region_ROM_start__ = __ICFEDIT_SE_Startup_region_ROM_start__ + 0x100;
__ICFEDIT_SE_Code_region_ROM_end__ = __ICFEDIT_SE_Startup_region_ROM_start__ + 0x52FF;
/* SE IF ROM: used to locate Secure Engine interface code out of firewall */
__ICFEDIT_SE_IF_region_ROM_start__ = __ICFEDIT_SE_Code_region_ROM_end__ + 1;
__ICFEDIT_SE_IF_region_ROM_end__ = __ICFEDIT_SE_IF_region_ROM_start__ + 0x66F;
/* SBSFU Code region */
__ICFEDIT_SB_region_ROM_start__ = __ICFEDIT_SE_IF_region_ROM_end__ + 0x1;
__ICFEDIT_SB_region_ROM_end__ = 0x0800FFFF;
SE_Entry_Secure_ROM_Region_Length = __ICFEDIT_SE_CallGate_region_ROM_end__ - __ICFEDIT_SE_CallGate_region_ROM_start__ + 1;
SE_Key_region_ROM_Length = __ICFEDIT_SE_Key_region_ROM_end__ - __ICFEDIT_SE_Key_region_ROM_start__ + 1;
SE_Startup_region_ROM_Length = __ICFEDIT_SE_Code_nokey_region_ROM_start__ - __ICFEDIT_SE_Startup_region_ROM_start__ ;
SE_ROM_region_Length = __ICFEDIT_SE_Code_region_ROM_end__ - __ICFEDIT_SE_Code_nokey_region_ROM_start__ + 1;
SE_IF_region_ROM_Length = __ICFEDIT_SE_IF_region_ROM_end__ - __ICFEDIT_SE_IF_region_ROM_start__ + 1;
SB_ROM_region_Length = __ICFEDIT_SB_region_ROM_end__ - __ICFEDIT_SB_region_ROM_start__ + 1;
/* SE stack is placed 1st in RAM, stack overflow does not write on other RAM area */
__ICFEDIT_SE_region_RAM_start__ = 0x20000000;
__ICFEDIT_SE_region_RAM_stack_top__ = 0x20000400;
__ICFEDIT_SE_region_RAM_end__ = 0x20000FFF;
/* SBSFU RAM1 region */
__ICFEDIT_SB_region_RAM_start__ = 0x20041000;
__ICFEDIT_SB_region_RAM_end__ = 0x2007FFFF;
SE_RAM_region_Length = __ICFEDIT_SE_region_RAM_end__ - __ICFEDIT_SE_region_RAM_stack_top__ + 1;
SB_RAM_region_Length = __ICFEDIT_SB_region_RAM_end__ - __ICFEDIT_SB_region_RAM_start__ + 1;
MEMORY
{
SE_Entry_Secure_ROM_Region (rx) : ORIGIN = __ICFEDIT_SE_CallGate_region_ROM_start__, LENGTH = SE_Entry_Secure_ROM_Region_Length
SE_Key_region_ROM (rx) : ORIGIN = __ICFEDIT_SE_Key_region_ROM_start__, LENGTH = SE_Key_region_ROM_Length
SE_Startup_region_ROM (rx) : ORIGIN = __ICFEDIT_SE_Startup_region_ROM_start__, LENGTH = SE_Startup_region_ROM_Length
SE_ROM_region (rx) : ORIGIN = __ICFEDIT_SE_Code_nokey_region_ROM_start__, LENGTH = SE_ROM_region_Length
SE_IF_region_ROM (rx) : ORIGIN = __ICFEDIT_SE_IF_region_ROM_start__, LENGTH = SE_IF_region_ROM_Length
SB_ROM_region (rx) : ORIGIN = __ICFEDIT_SB_region_ROM_start__, LENGTH = SB_ROM_region_Length
SE_RAM_region (xrw) : ORIGIN = __ICFEDIT_SE_region_RAM_stack_top__, LENGTH = SE_RAM_region_Length
SB_RAM_region (xrw) : ORIGIN = __ICFEDIT_SB_region_RAM_start__, LENGTH = SB_RAM_region_Length
}
mapping_fwimg.ld
/* swap (24 kbytes) */
__ICFEDIT_SWAP_start__ = 0x081FA000;
__ICFEDIT_SWAP_end__ = 0x081FFFFF;
/* Active slot #1 (960 kbytes)*/
__ICFEDIT_SLOT_Active_1_start__ = 0x08106000;
__ICFEDIT_SLOT_Active_1_end__ = 0x081F5FFF;
__ICFEDIT_SLOT_Active_1_header__ = __ICFEDIT_SLOT_Active_1_start__;
/* Dwl slot #1 (960 kbytes)*/
__ICFEDIT_SLOT_Dwl_1_start__ = 0x08010000;
__ICFEDIT_SLOT_Dwl_1_end__ = 0x080FFFFF;
2022-03-28 12:27 AM
Hello,
maybe a first thing to verify is that your application does not try to reuse the SRAM protected by Firewall:
The RAM in your application must start after this protected area:
define symbol __ICFEDIT_region_RAM_start__ = __ICFEDIT_SE_region_RAM_end__ + 1;
This is a first point to verify.
2022-03-28 01:45 AM
Hello @RBurg.2,
STM32L4+ family has an issue with firewall implementation that is a bit tricky.
Errata sheet describes issue.
I would suggest starting from STM32L5S5 project memory setup provided in the SBSFU package.
This chip is used for more complex secureboot setups using KMS and STSAFE but you should be able, at least to start from the same RAM memory mapping which is key.
There are some comments in linker file that can help you understand the limitations.
Best regards
Jocelyn
2022-03-28 02:45 AM
@Jocelyn RICARD : yes, can be this indeed.
@RBurg.2 : you can try to confirm this by not using SRAM1 (use SRAM2 and SRAM3 for your application) or you can implement a first fetch into another RAM before starting your application.
2022-03-28 05:45 PM
Thank you both for taking a look. It looks like that L4 firewall issue. I found this firewall issues post which you had posted the affected memory regions. Unfortunately, I use most of the RAM as double frame buffers with touchgfx so I don't have an option to only use SRAM2 and 3.
I did have some success by configuring my application ram (128kB) to start in SRAM3 (0x20041000) and placing one frame buffer at the start of SRAM1 after the SE region and the other at the end of SRAM3. Interestingly, in this configuration my application booted (expected), and even ran with a bouncing line test screen and basic screen transitions, but cover style screen transitions often caused a firewall fault (and they are my main transition type).
This has got me thinking that the full SFU firewall is overkill as I only need to do encrypted updates and protect from app code readout. Maybe implementing MPU protection for just the cryptography key would be a better option... What do you think?
2022-03-28 11:27 PM
Did you also try to make a first SRAM fetch out of the LSB problematic area ?
After this first "safe" fetch, any fetch should be fine according to the errata sheet.
MPU protection only is not so trivial, because to be on the safe side, you need most of your application to run in non-privileged mode. If you can achieve this, that's a possibility. But there will still be a difference : any master on the bus (DMA) can bypass the MPU protection.
If you do not need runtime services there may be another option:
In your application : Firewall protects the key in FLASH but not the SRAM1.
The key is never loaded in SRAM1 again.
But, this change does not bring the same security strength as the nominal implementation.
I would prefer giving a try with the workaround by adding a "fake" SRAM fetch when entering the application:
" avoid accessing SRAM1 before any access to an address outside the protected SRAM1 area, of which the 18 LSBs correspond to an address within the protected SRAM1 area. "
2022-03-29 02:25 PM
With my original memory configurations (app ram at __ICFEDIT_SE_region_RAM_end__ + 1) I added that specific fetch to SRAM outside of the problematic area at the start of main() and after SE_APP_ValidateFw() with no success; firewall reset at that same GPIOx->MODER write.
volatile uint32_t fetch = *(uint32_t*)0x20081200;
I tried a few memory locations in SRAM1 and SRAM3. Including checking that fetching from 0x20080200 does indeed cause the spurious firewall reset. Side note: I interpret the errata line "avoid accessing SRAM1 before any access to an address outside the protected SRAM1 area, of which the 18 LSBs correspond to an address within the protected SRAM1 area" to mean: any accesses to SRAM1 resets the problem with accessing locations that share the 18 LSBs of the protected area, so "don't use SRAM 1 for app code and you can use the full SRAM2 and 3" or "use SRAM1, 2, and 3 but not the locations in SRAM2 and 3 that the 18LSBs match the protected addresses".
Thanks for the description about the alternate method, good point that I can remove the firewall protections only for SRAM1. I would prefer if the full firewall worked too but it is what it is.
2022-03-30 12:47 AM
You can find an example of the dummy RAM access in the v2.6.0 of X-CUBE-SBSFU in the project dedicated to L4S5I.
For instance in 2_Images_KMS:
In Linker common define a zone for dummy access:
/* SBSFU RAM dummy memory access: See errata sheet: ES0393 - Rev 6 - October 2019 : */
/* - select another RAM bank than the RAM bank used by FWALL */
/* AND */
/* - select a memory range with 18 LSB outside the 18 LSB range protected by FWALL */
define exported symbol __ICFEDIT_SB_region_RAM_dummy_access_start__ = 0x20030000;
define exported symbol __ICFEDIT_SB_region_RAM_dummy_access_end__ = __ICFEDIT_SB_region_RAM_dummy_access_start__ + 3;
Then in SBSFU, place some code in this zone:
place in SB_RAM_dummy_access_region {section .SB_dummy_mem_access};
Then, each time you leave the Firewall, do this:
void SE_ExitSecureMode(uint32_t PrimaskBit)
{
SFU_LL_DummyAccess();
#if !defined(CKS_ENABLED)
/* Re-enable the interrupts */
__set_PRIMASK(PrimaskBit);
__enable_irq();
#endif /* !CKS_ENABLED */
}
with:
/* External variables --------------------------------------------------------*/
extern __IO uint32_t DummyMemAccess;
#if defined(SFU_LOW_LEVEL_C)
/*
* See errata sheet: ES0393 - Rev 6 - October 2019
* DummyMemAccess variable used in se_interface_exception_***.c to force data access outside FWALL protected SRAM1
* and outside the 18 LSB range protected by FWALL.
*/
#if defined ( __ICCARM__ )
__IO uint32_t DummyMemAccess @ ".SB_dummy_mem_access";
#elif defined ( __CC_ARM ) || defined(__GNUC__) || defined(__ARMCC_VERSION)
__IO uint32_t DummyMemAccess __attribute__((section(".SB_dummy_mem_access")));
#endif /* defined ( __ICCARM__ )*/
#endif /* defined(SFU_LOW_LEVEL_C) */
/*
* See errata sheet: ES0393 - Rev 6 - October 2019
* DummyMemAccess variable used in se_interface_exception_***.c to force data access outside FWALL protected SRAM1
* and outside the 18 LSB range protected by FWALL.
*/
#define SFU_LL_DummyAccess() \
do{ \
DummyMemAccess = 0xAAAAAAAA; \
}while(0)
2022-03-30 03:41 PM
Thanks for the note: I implemented that dummy access as in L4S5I/2_Images_KMS project, and verified in debug mode the location of the DummyMemAccess variable and that it was being written to after exiting the secure mode. It didn't have any affect on either of my framebuffer setups but I think I understand why.
The memory setup for that KMS project protects a large section of SRAM1 with firewall. And the SB region lower 18 bits overlaps with the protected SE region so the dummy access is required to not get firewall exceptions inside the SBSFU. The application in that project only uses SRAM 2 and 3. I start my SB region at 0x20041000, just because of example code, so that dummy access to a non-SRAM1 region without the lower 18 bit overlap happens naturally after exiting secure mode. It really looks like accesses to any of SRAM1 during application is what is causing the problem.
2022-04-08 05:28 PM
Hi @Fred
Today I had some time to test your suggestion of not having the firewall protect RAM. Interestingly I am getting a firewall reset on touchgfx cover transitions, same behavior as when I arranged my RAM so that only the framebuffer sections overlapped with the problematic RAM areas, frame buffer 1 - app ram - frame buffer 2. So it looks like full firewall protection might work if we can nail down this reset in touchgfx issue...
I disabled firewall in RAM by setting the init structure volatile segments to zero in sfu_low_level_security.c. My code boots into the application with the app ram in the problematic area then 2 frame buffers after).
FWALL_InitStruct.VDataSegmentStartAddress = 0;
FWALL_InitStruct.VDataSegmentLength = 0;
I tried to look into the cause of the reset, but without an interrupt to break on it is hard to nail it down. It isn't explicitly the cover transitions as some of them work every time, some are 50/50, and some always fail. When I step through the screen transition that it happens consistently on (breakpointing on view setup functions and high rate OS tasks, faster than screen refresh), the reset does not occur, and once the transition is complete it is stable. Do you have any suggestions on how to debug these firewall resets?
Thanks,