at91samd: add erase/secure commands, minor fix
Reference code for the SAMD2x disables caching in the NVM controller when issuing NVM commands. Let's do this as well to be consistent and safer. Add a "chip-erase" for the Atmel SAMD targets that issues a complete Chip Erase via the Device Service Unit (DSU). This can be used to "unlock" or otherwise unbrick a chip that can't be halted or inspected, allowing the user to reflash with new firmware. Add a "set-security" command which issues an SSB. Once that's done and the device is power-cycled, the flash cannot be written to until a "chip-erase" is issued. The chip-erase cannot be issued by openocd at this time because the device will not respond to a request for the DAP IDCODE. Change-Id: I80122f0bbf7e3aedffe052c1e77d69dc2dba25ed Signed-off-by: Andrey Yurovsky <yurovsky@gmail.com> Reviewed-on: http://openocd.zylin.com/2239 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>__archive__
parent
ec9ccaa288
commit
592d0d514d
|
@ -5023,6 +5023,30 @@ flash bank $_FLASHNAME aduc702x 0 0 0 0 $_TARGETNAME
|
||||||
@end example
|
@end example
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@anchor{at91samd}
|
||||||
|
@deffn {Flash Driver} at91samd
|
||||||
|
@cindex at91samd
|
||||||
|
|
||||||
|
@deffn Command {at91samd chip-erase}
|
||||||
|
Issues a complete Flash erase via the Device Service Unit (DSU). This can be
|
||||||
|
used to erase a chip back to its factory state and does not require the
|
||||||
|
processor to be halted.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {at91samd set-security}
|
||||||
|
Secures the Flash via the Set Security Bit (SSB) command. This prevents access
|
||||||
|
to the Flash and can only be undone by using the chip-erase command which
|
||||||
|
erases the Flash contents and turns off the security bit. Warning: at this
|
||||||
|
time, openocd will not be able to communicate with a secured chip and it is
|
||||||
|
therefore not possible to chip-erase it without using another tool.
|
||||||
|
|
||||||
|
@example
|
||||||
|
at91samd set-security enable
|
||||||
|
@end example
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@anchor{at91sam3}
|
@anchor{at91sam3}
|
||||||
@deffn {Flash Driver} at91sam3
|
@deffn {Flash Driver} at91sam3
|
||||||
@cindex at91sam3
|
@cindex at91sam3
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#define SAMD_NUM_SECTORS 16
|
#define SAMD_NUM_SECTORS 16
|
||||||
|
|
||||||
#define SAMD_FLASH ((uint32_t)0x00000000) /* physical Flash memory */
|
#define SAMD_FLASH ((uint32_t)0x00000000) /* physical Flash memory */
|
||||||
|
#define SAMD_PAC1 0x41000000 /* Peripheral Access Control 1 */
|
||||||
#define SAMD_DSU 0x41002000 /* Device Service Unit */
|
#define SAMD_DSU 0x41002000 /* Device Service Unit */
|
||||||
#define SAMD_NVMCTRL 0x41004000 /* Non-volatile memory controller */
|
#define SAMD_NVMCTRL 0x41004000 /* Non-volatile memory controller */
|
||||||
|
|
||||||
|
@ -295,47 +296,13 @@ static int samd_probe(struct flash_bank *bank)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int samd_protect(struct flash_bank *bank, int set, int first, int last)
|
static bool samd_check_error(struct target *target)
|
||||||
{
|
|
||||||
int res;
|
|
||||||
struct samd_info *chip = (struct samd_info *)bank->driver_priv;
|
|
||||||
|
|
||||||
res = ERROR_OK;
|
|
||||||
|
|
||||||
for (int s = first; s <= last; s++) {
|
|
||||||
if (set != bank->sectors[s].is_protected) {
|
|
||||||
/* Load an address that is within this sector (we use offset 0) */
|
|
||||||
res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR,
|
|
||||||
s * chip->sector_size);
|
|
||||||
if (res != ERROR_OK)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
/* Tell the controller to lock that sector */
|
|
||||||
|
|
||||||
uint16_t cmd = (set) ?
|
|
||||||
SAMD_NVM_CMD(SAMD_NVM_CMD_LR) :
|
|
||||||
SAMD_NVM_CMD(SAMD_NVM_CMD_UR);
|
|
||||||
|
|
||||||
res = target_write_u16(bank->target,
|
|
||||||
SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA,
|
|
||||||
cmd);
|
|
||||||
if (res != ERROR_OK)
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
samd_protect_check(bank);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool samd_check_error(struct flash_bank *bank)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
bool error;
|
bool error;
|
||||||
uint16_t status;
|
uint16_t status;
|
||||||
|
|
||||||
ret = target_read_u16(bank->target,
|
ret = target_read_u16(target,
|
||||||
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status);
|
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status);
|
||||||
if (ret != ERROR_OK) {
|
if (ret != ERROR_OK) {
|
||||||
LOG_ERROR("Can't read NVM status");
|
LOG_ERROR("Can't read NVM status");
|
||||||
|
@ -356,7 +323,7 @@ static bool samd_check_error(struct flash_bank *bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the error conditions by writing a one to them */
|
/* Clear the error conditions by writing a one to them */
|
||||||
ret = target_write_u16(bank->target,
|
ret = target_write_u16(target,
|
||||||
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status);
|
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status);
|
||||||
if (ret != ERROR_OK)
|
if (ret != ERROR_OK)
|
||||||
LOG_ERROR("Can't clear NVM error conditions");
|
LOG_ERROR("Can't clear NVM error conditions");
|
||||||
|
@ -364,25 +331,88 @@ static bool samd_check_error(struct flash_bank *bank)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
|
||||||
|
{
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
LOG_ERROR("Target not halted");
|
||||||
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read current configuration. */
|
||||||
|
uint16_t tmp = 0;
|
||||||
|
int res = target_read_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB,
|
||||||
|
&tmp);
|
||||||
|
if (res != ERROR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/* Set cache disable. */
|
||||||
|
res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB,
|
||||||
|
tmp | (1<<18));
|
||||||
|
if (res != ERROR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/* Issue the NVM command */
|
||||||
|
int res_cmd = target_write_u16(target,
|
||||||
|
SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, SAMD_NVM_CMD(cmd));
|
||||||
|
|
||||||
|
/* Try to restore configuration, regardless of NVM command write
|
||||||
|
* status. */
|
||||||
|
res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, tmp);
|
||||||
|
|
||||||
|
if (res_cmd != ERROR_OK)
|
||||||
|
return res_cmd;
|
||||||
|
|
||||||
|
if (res != ERROR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/* Check to see if the NVM command resulted in an error condition. */
|
||||||
|
if (samd_check_error(target))
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int samd_protect(struct flash_bank *bank, int set, int first, int last)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct samd_info *chip = (struct samd_info *)bank->driver_priv;
|
||||||
|
|
||||||
|
res = ERROR_OK;
|
||||||
|
|
||||||
|
for (int s = first; s <= last; s++) {
|
||||||
|
if (set != bank->sectors[s].is_protected) {
|
||||||
|
/* Load an address that is within this sector (we use offset 0) */
|
||||||
|
res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR,
|
||||||
|
s * chip->sector_size);
|
||||||
|
if (res != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* Tell the controller to lock that sector */
|
||||||
|
res = samd_issue_nvmctrl_command(bank->target,
|
||||||
|
set ? SAMD_NVM_CMD_LR : SAMD_NVM_CMD_UR);
|
||||||
|
if (res != ERROR_OK)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
samd_protect_check(bank);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static int samd_erase_row(struct flash_bank *bank, uint32_t address)
|
static int samd_erase_row(struct flash_bank *bank, uint32_t address)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
/* Set an address contained in the row to be erased */
|
/* Set an address contained in the row to be erased */
|
||||||
res = target_write_u32(bank->target,
|
res = target_write_u32(bank->target,
|
||||||
SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1);
|
SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1);
|
||||||
if (res == ERROR_OK) {
|
|
||||||
/* Issue the Erase Row command to erase that row */
|
/* Issue the Erase Row command to erase that row */
|
||||||
res = target_write_u16(bank->target,
|
if (res == ERROR_OK)
|
||||||
SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA,
|
res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_ER);
|
||||||
SAMD_NVM_CMD(SAMD_NVM_CMD_ER));
|
|
||||||
|
|
||||||
/* Check (and clear) error conditions */
|
if (res != ERROR_OK) {
|
||||||
error = samd_check_error(bank);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res != ERROR_OK || error) {
|
|
||||||
LOG_ERROR("Failed to erase row containing %08" PRIx32, address);
|
LOG_ERROR("Failed to erase row containing %08" PRIx32, address);
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -488,7 +518,7 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = samd_check_error(bank);
|
error = samd_check_error(bank->target);
|
||||||
if (error)
|
if (error)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
@ -652,6 +682,51 @@ COMMAND_HANDLER(samd_handle_info_command)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(samd_handle_chip_erase_command)
|
||||||
|
{
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
/* Enable access to the DSU by disabling the write protect bit */
|
||||||
|
target_write_u32(target, SAMD_PAC1, (1<<1));
|
||||||
|
/* Tell the DSU to perform a full chip erase. It takes about 240ms to
|
||||||
|
* perform the erase. */
|
||||||
|
target_write_u8(target, SAMD_DSU, (1<<4));
|
||||||
|
|
||||||
|
command_print(CMD_CTX, "chip erased");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(samd_handle_set_security_command)
|
||||||
|
{
|
||||||
|
int res = ERROR_OK;
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
|
||||||
|
if (CMD_ARGC < 1 || (CMD_ARGC >= 1 && (strcmp(CMD_ARGV[0], "enable")))) {
|
||||||
|
command_print(CMD_CTX, "supply the \"enable\" argument to proceed.");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
LOG_ERROR("Target not halted");
|
||||||
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_SSB);
|
||||||
|
|
||||||
|
/* Check (and clear) error conditions */
|
||||||
|
if (res == ERROR_OK)
|
||||||
|
command_print(CMD_CTX, "chip secured on next power-cycle");
|
||||||
|
else
|
||||||
|
command_print(CMD_CTX, "failed to secure chip");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct command_registration at91samd_exec_command_handlers[] = {
|
static const struct command_registration at91samd_exec_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.name = "info",
|
.name = "info",
|
||||||
|
@ -660,6 +735,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
|
||||||
.help = "Print information about the current at91samd chip"
|
.help = "Print information about the current at91samd chip"
|
||||||
"and its flash configuration.",
|
"and its flash configuration.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "chip-erase",
|
||||||
|
.handler = samd_handle_chip_erase_command,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.help = "Erase the entire Flash by using the Chip"
|
||||||
|
"Erase feature in the Device Service Unit (DSU).",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "set-security",
|
||||||
|
.handler = samd_handle_set_security_command,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.help = "Secure the chip's Flash by setting the Security Bit."
|
||||||
|
"This makes it impossible to read the Flash contents."
|
||||||
|
"The only way to undo this is to issue the chip-erase"
|
||||||
|
"command.",
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue