Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

target/nrf91: add mass_erase and recovery probe #1785

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

maxd-nordic
Copy link

@maxd-nordic maxd-nordic commented Mar 15, 2024

Detailed description

  • add nrf91 rescue probe
  • add nrf91 mass erase
  • add detailed nrf91 identification
  • add nrf91 UICR writing
  • add UICR APPROTECT initialization
  • add UICR overwrite warning (recommend mass erase if value cannot be written)

Your checklist for this pull request

Closing issues

fixes #1778

@maxd-nordic
Copy link
Author

@dragonmux I added most of the missing parts for nrf91. Maybe you could give me pointers on how to handle the TODOs? The UICR APPROTECT part basically needs to do a flash write after each mass-erase to tell the "hardenend APPROTECT" system not to lock down the device after the next reset. This is also relevant for nRF53 support.

@dragonmux dragonmux added this to the v2.0 release milestone Mar 15, 2024
@dragonmux dragonmux added the Enhancement General project improvement label Mar 15, 2024
@dragonmux
Copy link
Member

The post-Flash write and mass-erase UICR fix-up sounds like something that should be a target exit_flash_mode function and then call that at the end of the nRF91 mass erase implementation too - that should take care of the problem.

Copy link
Member

@dragonmux dragonmux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done an initial review, there are a lot of interesting details here that we're very grateful to get to know about, like the TARGETID revision number being used for once in a part and having bearing on the part number.

Please don't forget to run clang-format on the contribution and fix the few hex constant capitalisation lints - the clang-format lint pass is currently broken but we are still enforcing it.


return true;
}

int nrf91_dp_prepare(adiv5_debug_port_s *const dp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses int for a boolean return value, please use bool instead. We're in C11 here, so <stdbool.h> functions properly, giving access to clean error returns. Note, we would strongly prefer true to mean success otherwise the caller side logic becomes cumbersome and difficult to comprehend, which is error-prone.

ctrl_ap.idr = adiv5_ap_read(&ctrl_ap, ADIV5_AP_IDR);

if (ahb_ap.idr != AHB_AP_IDR_EXPECTED) {
DEBUG_ERROR("nRF91: AHB-AP IDR is 0x%08x, expected 0x84770001\n", ahb_ap.idr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use PRIx32 for the format specifier, not x, as idr is a uint32_t and so this doesn't work on Xtensa or several desktop platforms.

eg: DEBUG_ERROR("nRF91: AHB-AP IDR is 0x%08" PRIx32 ", expected 0x84770001\n", ahb_ap.idr);

It would also be better for Flash space if the expected value were formatted in the same way.

@@ -98,5 +98,6 @@ bool stm32mp15_cm4_probe(target_s *target);
bool zynq7_probe(target_s *target);

void lpc55_dp_prepare(adiv5_debug_port_s *dp);
int nrf91_dp_prepare(adiv5_debug_port_s *const dp);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const here will be ignored by the compiler. This should be a clang-tidy lint. Having it in the function definitions is correct though.


ap->dp->mem_read(ap, buf, 0x00FF0140, 3*sizeof(uint32_t));

gdb_outf("nRF%04x %4s%4s detected!\n", buf[0], (const char *)&buf[2], (const char *)&buf[1]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be fine as a DEBUG_* statement, but should be avoided in the scan output as it's not guaranteed where exactly this will end up in the information display, while also duplicating the information to the user from the target->driver strings.

Same comment as below on uint32_t needing PRIx32.

}

bool nrf91_probe(target_s *target)
{
adiv5_access_port_s *ap = cortex_ap(target);

if (ap->dp->version < 2U)
if (ap->dp->version < 2U || ap->dp->target_partno != 0x90U)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid a magic number here - 0x90U should ideally be #define'd above as, eg, ID_NRF91 so it's clearer what it is and where it comes from. Ideally a reference to the TRM section and page this information can be found from would be great, but we know that's not always possible.

Comment on lines 19 to 28
#define CTRL_AP_RESET ADIV5_AP_REG(0x000)
#define CTRL_AP_ERASEALL ADIV5_AP_REG(0x004)
#define CTRL_IDR_EXPECTED 0x12880000
#define AHB_AP_IDR_EXPECTED 0x84770001
#define CTRL_AP_ERASEALLSTATUS ADIV5_AP_REG(0x008)

#define UICR_APPROTECT 0x00FF8000U
#define UICR_SECUREAPPROTECT 0x00FF802CU
#define UICR_APPROTECT_UNPROTECT_VAL 0x50FA50FAU
#define UICR_ERASED_VAL 0xFFFFFFFFU
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please prefix these with NRF91_ to maintain the macro prefixing for the file.




static bool nrf91_rescue_do_recover(target_s *t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid single letter variable naming for this, please use target instead. Likewise for nrf91_rescue_probe() on return from target_new().

This is the style we've been gradually moving the whole code base to.

//check if CSW DEVICEEN is set
struct adiv5_access_port ahb_ap = *ap;
ahb_ap.apsel = 0x0U;
uint32_t csw = ap->dp->ap_read(&ahb_ap, ADIV5_AP_CSW);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be made const, so lets do so.

static bool nrf91_uicr_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
{
/* can't erase UICR without a mass-erase */
/* TODO: this will silently fail if you actually need to erase */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easiest way to fix this would be to gdb_outf() a message like "Skipping UICR erase, operation requires mass erase". If there's a good way to blank check the UICR that can be used to have this routine return true with no message, and then return false if the UICR is not blank.

@maxd-nordic
Copy link
Author

The post-Flash write and mass-erase UICR fix-up sounds like something that should be a target exit_flash_mode function and then call that at the end of the nRF91 mass erase implementation too - that should take care of the problem.

Thanks! Got it to work now. Just putting it in exit_flash_mode is enough IMO because a blank app will also cause the protection to engage.

@maxd-nordic maxd-nordic force-pushed the nrf91-recover branch 3 times, most recently from 2d81846 to b1391c6 Compare March 18, 2024 15:57
@dragonmux
Copy link
Member

The reason we said "and at the end of the mass erase function" is that it's up to the target support too override target->mass_erase with its own implementation (it's NULL by default) and the implementation will only do what you put in the function - so if you need to call target->exit_flash_mode then you'll need to include that in the implementation specifically. We'd be concerned for newly mass erased parts winding up protected without it.

@maxd-nordic
Copy link
Author

The reason we said "and at the end of the mass erase function" is that it's up to the target support too override target->mass_erase with its own implementation (it's NULL by default) and the implementation will only do what you put in the function - so if you need to call target->exit_flash_mode then you'll need to include that in the implementation specifically. We'd be concerned for newly mass erased parts winding up protected without it.

nrfjprog flashes a placeholder app onto the target when doing recovery. Should I include that here as well? (it would use up quite a bit of space) Just writing UICR is not enough to keep protection off. The way it's implemented now, you can do a mass-erase and UICR is written alongside your app.

@maxd-nordic
Copy link
Author

I got to check the flash write function again - we can skip the readynext wait if the page is erased beforehand.

@maxd-nordic
Copy link
Author

@dragonmux I managed to make the app image much smaller and include it here - the target is now unprotected after mass-erase. :)

Signed-off-by: Maximilian Deubel <maximilian.deubel@nordicsemi.no>
@maxd-nordic
Copy link
Author

It might actually be better to implement target commands and show the CTRL-AP in any case. There is also a rarely-used feature that could lock mass-erase with a password.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement General project improvement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

extending nRF91 support
2 participants