at91samd: add erase/secure commands, minor fix 39/2239/7
authorAndrey Yurovsky <yurovsky@gmail.com>
Wed, 6 Aug 2014 21:28:37 +0000 (14:28 -0700)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 6 Oct 2014 11:56:27 +0000 (11:56 +0000)
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>
doc/openocd.texi
src/flash/nor/at91samd.c

index 0b50c65b817767730d5dbbc3190dd2832e4eeed8..7cba65dd0ca1d1bfd2f40ba947a222aa73f7cef6 100644 (file)
@@ -5023,6 +5023,30 @@ flash bank $_FLASHNAME aduc702x 0 0 0 0 $_TARGETNAME
 @end example
 @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}
 @deffn {Flash Driver} at91sam3
 @cindex at91sam3
index 7d25b4c5148853ce45cdac30fd584d612379b553..9eec0d0ead86b0a0aba89e831067f2a4f508485f 100644 (file)
@@ -27,6 +27,7 @@
 #define SAMD_NUM_SECTORS       16
 
 #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_NVMCTRL           0x41004000      /* Non-volatile memory controller */
 
@@ -295,47 +296,13 @@ static int samd_probe(struct flash_bank *bank)
        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 */
-
-                       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)
+static bool samd_check_error(struct target *target)
 {
        int ret;
        bool error;
        uint16_t status;
 
-       ret = target_read_u16(bank->target,
+       ret = target_read_u16(target,
                        SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status);
        if (ret != ERROR_OK) {
                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 */
-       ret = target_write_u16(bank->target,
+       ret = target_write_u16(target,
                        SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status);
        if (ret != ERROR_OK)
                LOG_ERROR("Can't clear NVM error conditions");
@@ -364,25 +331,88 @@ static bool samd_check_error(struct flash_bank *bank)
        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)
 {
        int res;
-       bool error = false;
 
        /* Set an address contained in the row to be erased */
        res = target_write_u32(bank->target,
                        SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1);
-       if (res == ERROR_OK) {
-               /* Issue the Erase Row command to erase that row */
-               res = target_write_u16(bank->target,
-                               SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA,
-                               SAMD_NVM_CMD(SAMD_NVM_CMD_ER));
 
-               /* Check (and clear) error conditions */
-               error = samd_check_error(bank);
-       }
+       /* Issue the Erase Row command to erase that row */
+       if (res == ERROR_OK)
+               res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_ER);
 
-       if (res != ERROR_OK || error)  {
+       if (res != ERROR_OK)  {
                LOG_ERROR("Failed to erase row containing %08" PRIx32, address);
                return ERROR_FAIL;
        }
@@ -488,7 +518,7 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address,
                        return res;
                }
 
-               error = samd_check_error(bank);
+               error = samd_check_error(bank->target);
                if (error)
                        return ERROR_FAIL;
 
@@ -652,6 +682,51 @@ COMMAND_HANDLER(samd_handle_info_command)
        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[] = {
        {
                .name = "info",
@@ -660,6 +735,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
                .help = "Print information about the current at91samd chip"
                        "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
 };
 

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)