2024-02-29 06:08 AM - edited 2024-02-29 06:15 AM
I am building a secure bootloader on the STM32H755, but I've bricked a couple of boards now trying to get the secure areas working.
I intended to use a "recovery" function on a button press to do a mass erase with protection removal. Then I can test enabling the secure areas via resetAndInitializeSecureAreas, and get back to square 1 nice and easy.
Except, it didn't work - I mass erased by device but the debugger doesn't work... So I assume the secure area is still present.
Before I brick yet another board, I'd like to check my assumptions.
If I follow the steps in "Flash mass erase with automatic protection-removal sequence", on the STM32H755, will that get me back to a "blank" device, with no secure regions?
Additionally, can I place the code that does that in RAM - IE in a non secure region? I ask because it feels wrong to erase flash while running from flash.
2024-02-29 06:19 AM
For reference my mass erase code:
// Unlock the control registers
if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) > 0) {
unlock_bank(1);
}
if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) > 0) {
unlock_bank(2);
}
// Unlock the option bytes
FLASH->OPTKEYR = 0x08192A3B;
FLASH->OPTKEYR = 0x4C5D6E7F;
__DSB();
// Remove PCROP araes
write_prot_area(&FLASH->PRAR_PRG1, (ProtectedArea_t){.start = 2, .end = 0});
write_prot_area(&FLASH->PRAR_PRG2, (ProtectedArea_t){.start = 2, .end = 0});
// Mark the PCROP areas as needing erasing during mass erase
SET_BIT(FLASH->PRAR_PRG1, FLASH_PRAR_DMEP);
SET_BIT(FLASH->PRAR_PRG2, FLASH_PRAR_DMEP);
// Remove secure memory areas
write_prot_area(&FLASH->SCAR_PRG1, (ProtectedArea_t){.start = 2, .end = 0});
write_prot_area(&FLASH->SCAR_PRG2, (ProtectedArea_t){.start = 2, .end = 0});
// Mark the secure memory areas as needing erasing during mass erase
SET_BIT(FLASH->SCAR_PRG1, FLASH_SCAR_DMES);
SET_BIT(FLASH->SCAR_PRG2, FLASH_SCAR_DMES);
// Remove write protection
FLASH->WPSN_PRG1 = 0xFF;
FLASH->WPSN_PRG2 = 0xFF;
// Trigger mass erase
SET_BIT(FLASH->OPTCR, FLASH_OPTCR_MER);
// Wait for completion
while ((READ_BIT(FLASH->SR1, FLASH_SR_QW) | READ_BIT(FLASH->SR2, FLASH_SR_QW)) > 0) {}
2024-02-29 07:27 AM
Hello @gpeh,
the way I found to address this was to create a small loader able to download an update to the target, so that if the regression didn't work I had the ability to load a new code to do it.
Here is the code I used some time ago on the STM32H755:
static void Regression(void)
{
uint32_t confirm;
HAL_StatusTypeDef error;
printf("Launching RDP regression. Requires RDPL1 already set\r\n");
printf("=================== WARNING =====================\r\n");
printf("Please wait 10s before reseting or unpluging the board\r\n");
printf("Confirm [y/n]\r\n");
confirm =ReadUserInput();
if (confirm != 'y')
{
return;
}
printf("Regression Launched ... please wait 10s\r\n");
__HAL_FLASH_CLEAR_FLAG_BANK1(FLASH_FLAG_ALL_ERRORS_BANK1);
__HAL_FLASH_CLEAR_FLAG_BANK1(FLASH_FLAG_ALL_ERRORS_BANK2);
HAL_FLASH_OB_Unlock();
FLASH->OPTSR_PRG = 0x1B9EAAF0; /* RDP level 0 */
FLASH->PRAR_PRG1 = 0x80000FFF; /* No PCROP */
FLASH->SCAR_PRG1 = 0x80000FFF; /* No secure area */
FLASH->WPSN_PRG1 = 0x000000FF; /* No WRP */
if ((error=HAL_FLASH_OB_Launch()) != HAL_OK)
{
printf("HAL_FLASH_OB_Launch failed. HAL_ERROR: %d Error code : %8.8lx ...\r\n", error, pFlash.ErrorCode);
printf("Flash->SR1: 0x%8.8lx\r\n", FLASH->SR1);
return;
}
// Shouldn't go past this point
if (HAL_FLASH_OB_Lock()!= HAL_OK)
{
printf("Error HAL_FLASH_OB_Lock\r\n");
return;
}
printf("RDP1 regression successful!");
}
I hope this will help
Best regards
Jocelyn
2024-02-29 07:33 AM
It's interesting that you're choosing to do an RDP regression instead of the mass erase procedure, I could certainly do that as well.
Is it okay for me to be running this code out of (non-secure) RAM? Or should this be in the secure region?
2024-02-29 10:12 AM
Hi @gpeh ,
I’m sorry I assumed you were doing the mass erase through regression. I didn’t take time to check your code.
There is no way to recover if you perform a simple mass erase. The secure area will not be removed. This sis normally described in the reference manual.
The code can be executed from user flash even outside secure area and from ram.
Yes it looks strange, this is like cutting the branch you are sitting on 😀 but it works !
The important point is that this regression is not reset safe because secure area removal is done at the end of the mass erase. So you must ensure no reset occurs during the regression otherwise secure area is not removed and no more code is available to remove it.
On this chip, mass erase takes quite long time, so make sure to wait at least 10s before trying to reconnect
Best regards
Jocelyn
2024-02-29 10:16 AM
Okay, I am pending on the QW bit in the flash status register, which I assumed would be enough to ensure the operation was completed. Then an NVIC reset is triggered. Perhaps I should just busy loop and manually wait 10s?
As an aside - what is the correct way to check whether I am in Secure mode or not? Do I just check the security bit + the bounds of the secure area option bytes? Or is there a better way? I did see the `SECURE` bit in SYSCFG_UR12 but I couldn't find any documentation about it.
2024-03-01 12:07 AM
Hi @gpeh ,
with the code I shared with you the OBLaunch triggers the regression if you have previously set RDP Level to 1.
No need to have something in the code for waiting, the code will not go any further.
The 10seconds waiting is before making anything to the board (reset, JTAG connection)
When you set the security bit, you are no more able to connect under reset to the target. Only hotplug is possible.
The security bit is FLASH_OPTSR_SECURITY bit.
Please have a look to this post where I provide an example based on STM32H753
Best regards
Jocelyn
2024-03-01 12:25 AM
Hi @Jocelyn RICARD , thank you.
I am developing without the HAL. However I can see that OB_Launch waits for the BUSY bit to be clear. And this post says that using the QW bit is equivalent, so that's fine.
And yes I have learnt that about the SECURITY bit (from a post I found from you, I think!). But what I mean is that just because the SECURITY bit is set, it does not mean that the secure user area is active - So I wondered if there was an easy way to check that. Right now I am just checking the SECURITY bit + the bounds of the secure area.
I will read through the H753 example you have provided, it looks much more approachable than SBSFU.
---
So the remaining confusion for me is that I believe my mass erase code *should* have worked. It worked for PCROP, and I think it should have worked for the secure area. Everything about it seems correct.
So maybe I was impatient and accidentally pressed reset again? And triggered the reset failure you have mentioned.
2024-03-01 01:35 AM
Hello @gpeh ,
even when not using HAL I would advise doing things in similar way.
But here this is not the issue obviously.
For checking the secure area, just reading the option bytes bounds of secure area is enough. Secure area cannot be set if security bit is not set anyway.
Regarding your last point, I've checked RM0399 rev4 p174 that describes how to remove all protections without the need to go through a RDP regression. I just discovered this specific sequence, so never experienced it.
So, yes one possibility is you didn't wait long enough that everything was finished.
When secure memory is set, even in RDP level 0 the jtag is automatically disabled at boot. So, if no code is available to reopen the jtag, you have no way to recover.
Best regards
Jocelyn
2024-03-01 02:35 AM
Hi @Jocelyn RICARD,
Thank you, I'm glad this is confirming that my approach is sensible.
It sounds like I perhaps need to just offer another nucleo board up for sacrifice!
I have one more question before diving back in, which maybe is not that easy to answer:
The mass erase description in RM0399 says this:
No other option bytes than the ones mentioned in the above sequence must be changed,
otherwise a simple mass erase is executed, no option byte change is performed and no
option change error is raised.
This process clears both the PCROP and Secure regions. Can I therefore assume if this successfully works on a PCROP region (which is much safer to test, no chance to brick!), that it really *is* doing a "mass erase with automatic protection-removal"? And therefore I can have high confidence that it will work on a Secure region?