Skip to main content
Associate
February 29, 2024
Question

Resetting an STM32H755 with secure area

  • February 29, 2024
  • 8 replies
  • 5579 views

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.

This topic has been closed for replies.

8 replies

gpehAuthor
Associate
February 29, 2024

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) {}
Jocelyn RICARD
ST Employee
February 29, 2024

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

gpehAuthor
Associate
February 29, 2024

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?

Jocelyn RICARD
ST Employee
February 29, 2024

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 :grinning_face: 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

Jocelyn RICARD
ST Employee
March 1, 2024

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

gpehAuthor
Associate
March 1, 2024

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.

Jocelyn RICARD
ST Employee
March 1, 2024

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

 

 

 

gpehAuthor
Associate
March 1, 2024

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?

Jocelyn RICARD
ST Employee
March 1, 2024

Hello @gpeh ,

yes this would also be my approach.

I'm too busy right now to dig into this but this is in my toto list ! If you can wait until next week I can check on my side.

Best regards

Jocelyn

gpehAuthor
Associate
March 1, 2024

@Jocelyn RICARD 

It works! The mass erase sequence I described previously does remove the secure areas correctly, and I can now recover a secured device. I must have previously interrupted it with a reset.

 

My issue now is that `exitSecureArea` does not jump to my application code, instead I get a hardfault. But it does reopen the JTAG, so that's nice at least. One more thing to work out I suppose!

---

For anyone arriving at this post in future, here is the process I followed to get here:

- Write the mass erase code

- Verify that it can recover a PCROP area

- Set your mass erase code to write a recognisable number to the secure area bounds

- Verify that your mass erase *does* write those numbers, and sets the DMES bit

- If that's all true, you should be able to clear a secure area and you can go ahead and hope you don't brick.

gpehAuthor
Associate
March 1, 2024

@Jocelyn RICARDI now only have one remaining problem, which is that `exitSecureArea` cannot jump to my application correctly.

I can jump to an application also located in bank 1, but if I try to jump to an application in bank 2 it fails.

This is in contrast to the "manual jump", which works just fine.

__NO_RETURN static void jump_to_address(void* addr) {
 if (secure_mode_is_enabled()) {
 RSS->exitSecureArea((uint32_t)addr, REOPEN_JTAG);
 }

 BootloaderVectorTable_t const* const vectors = (BootloaderVectorTable_t*)addr;

 __set_MSP(vectors->sp);
 SCB->VTOR = (uint32_t)addr;
 __ISB();
 vectors->rst();
 hcf();
}

 

Is there a restriction that `exitSecureArea` can only jump to applications located in the same bank?

gpehAuthor
Associate
March 1, 2024

I also notice that if you `exitSecureArea` and reopen the jtag, then you can read out all the secure area flash which I find very surprising - and in contradiction to the reference manual:

 


Only the Arm ® Cortex ®
-M7 core executing ST secure library or user secure application
can access it (Master ID filtering). In all other cases, accessing the secure-only area is
illegal (see below)

Is this correct or have I done something wrong? If I am correct I assume I will need to also use RDP level 1 to protect readout of the secure area via debugger...

Jocelyn RICARD
ST Employee
March 1, 2024

Hello @gpeh ,

About jump in second bank I will need to double check on my side.

About your second point, did you configure the secure area or just used the exitSecureArea function ? If secure area is setup, it should be closed.

Best regards

Jocelyn

gpehAuthor
Associate
March 1, 2024

I configured the secure area using `resetAndInitializeSecureAreas`.

SECURITY bit is 1, secure bounds are valid:

gpeh_0-1709304693167.png

 

But as you can see I can happily read out the memory region

gpeh_1-1709304707712.png

 

Jocelyn RICARD
ST Employee
March 1, 2024

Hi @gpeh ,

I made a quick test:

  • I can confirm that exitSecureArea supports second parameter to control jtag reopening. After launching following code, the JTAG is open

 

RSS_API->exitSecureArea(userAppAddress, RSS_ENABLE_JTAG_AT_EXIT);​

 

  • If the user application is in second bank the call is also working fine. 
  • When I set first sector of bank1 as secure memory, then launch user application on second bank, and then connect in hotplug mode with programmer, I see zeros displayed when I read first sector of first bank. All remaining flash is visible. This is expected behaviour of secure memory.

All this keeping RDP level to 0

For testing, I'm using a Nucleo-H755ZI-Q  NUH55AIQ$AT3 with revision V.

Best regards

Jocelyn

 

Best regards

Jocelyn

 

Pavel A.
Super User
March 1, 2024

@Jocelyn RICARD Excuse me for jumping in. What exactly means RSS_ENABLE_JTAG_AT_EXIT: ability to connect debugger in hotplug mode or also ability to read the secure memory from debugger interface?

 

Jocelyn RICARD
ST Employee
March 1, 2024

Hi @Pavel A. 

this additional parameter to the call of RSS function exitSecureArea will only make JTAG available for hotplug connection.

The secure memory is locked when cpu executes code in user flash: no way to access it from code neither debugger.

Best regards 

Jocelyn

 

 

 

gpehAuthor
Associate
March 4, 2024

@Jocelyn RICARD 

Running again, I now see the memory zeroed out - I don't know why that was acting weird before, perhaps just something in CubeProgrammer.

 

And my bank1/bank2 issue was because `exitSecureArea` does not set the vector table location. My image in bank 1 set its own VTOR at startup - my image in bank 2 did not. So jumping to the bank 2 image failed.

 

Thank you for all your help Jocelyn, it's been a real slog until you got involved. Beste!