aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/flash/nor/kinetis.c352
-rw-r--r--src/target/arm_adi_v5.c182
-rw-r--r--tcl/target/kl25.cfg8
-rw-r--r--tcl/target/kl25z_hla.cfg8
4 files changed, 350 insertions, 200 deletions
diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c
index 86785905..42677a38 100644
--- a/src/flash/nor/kinetis.c
+++ b/src/flash/nor/kinetis.c
@@ -35,6 +35,7 @@
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
+#include <target/cortex_m.h>
/*
* Implementation Notes
@@ -196,6 +197,267 @@ struct kinetis_flash_bank {
} flash_class;
};
+
+
+#define MDM_REG_STAT 0x00
+#define MDM_REG_CTRL 0x04
+#define MDM_REG_ID 0xfc
+
+#define MDM_STAT_FMEACK (1<<0)
+#define MDM_STAT_FREADY (1<<1)
+#define MDM_STAT_SYSSEC (1<<2)
+#define MDM_STAT_SYSRES (1<<3)
+#define MDM_STAT_FMEEN (1<<5)
+#define MDM_STAT_BACKDOOREN (1<<6)
+#define MDM_STAT_LPEN (1<<7)
+#define MDM_STAT_VLPEN (1<<8)
+#define MDM_STAT_LLSMODEXIT (1<<9)
+#define MDM_STAT_VLLSXMODEXIT (1<<10)
+#define MDM_STAT_CORE_HALTED (1<<16)
+#define MDM_STAT_CORE_SLEEPDEEP (1<<17)
+#define MDM_STAT_CORESLEEPING (1<<18)
+
+#define MEM_CTRL_FMEIP (1<<0)
+#define MEM_CTRL_DBG_DIS (1<<1)
+#define MEM_CTRL_DBG_REQ (1<<2)
+#define MEM_CTRL_SYS_RES_REQ (1<<3)
+#define MEM_CTRL_CORE_HOLD_RES (1<<4)
+#define MEM_CTRL_VLLSX_DBG_REQ (1<<5)
+#define MEM_CTRL_VLLSX_DBG_ACK (1<<6)
+#define MEM_CTRL_VLLSX_STAT_ACK (1<<7)
+
+#define MDM_ACCESS_TIMEOUT 3000 /* iterations */
+
+static int kinetis_mdm_write_register(struct adiv5_dap *dap, unsigned reg, uint32_t value)
+{
+ int retval;
+ LOG_DEBUG("MDM_REG[0x%02x] <- %08" PRIX32, reg, value);
+
+ retval = dap_queue_ap_write(dap, reg, value);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: failed to queue a write request");
+ return retval;
+ }
+
+ retval = dap_run(dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: dap_run failed");
+ return retval;
+ }
+
+
+ return ERROR_OK;
+}
+
+static int kinetis_mdm_read_register(struct adiv5_dap *dap, unsigned reg, uint32_t *result)
+{
+ int retval;
+ retval = dap_queue_ap_read(dap, reg, result);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: failed to queue a read request");
+ return retval;
+ }
+
+ retval = dap_run(dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: dap_run failed");
+ return retval;
+ }
+
+ LOG_DEBUG("MDM_REG[0x%02x]: %08" PRIX32, reg, *result);
+ return ERROR_OK;
+}
+
+static int kinetis_mdm_poll_register(struct adiv5_dap *dap, unsigned reg, uint32_t mask, uint32_t value)
+{
+ uint32_t val;
+ int retval;
+ int timeout = MDM_ACCESS_TIMEOUT;
+
+ do {
+ retval = kinetis_mdm_read_register(dap, reg, &val);
+ if (retval != ERROR_OK || (val & mask) == value)
+ return retval;
+
+ alive_sleep(1);
+ } while (timeout--);
+
+ LOG_DEBUG("MDM: polling timed out");
+ return ERROR_FAIL;
+}
+
+/*
+ * This function implements the procedure to mass erase the flash via
+ * SWD/JTAG on Kinetis K and L series of devices as it is described in
+ * AN4835 "Production Flash Programming Best Practices for Kinetis K-
+ * and L-series MCUs" Section 4.2.1
+ */
+COMMAND_HANDLER(kinetis_mdm_mass_erase)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+
+ int retval;
+ const uint8_t original_ap = dap->ap_current;
+
+ /*
+ * ... Power on the processor, or if power has already been
+ * applied, assert the RESET pin to reset the processor. For
+ * devices that do not have a RESET pin, write the System
+ * Reset Request bit in the MDM-AP control register after
+ * establishing communication...
+ */
+ dap_ap_select(dap, 1);
+
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, MEM_CTRL_SYS_RES_REQ);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /*
+ * ... Read the MDM-AP status register until the Flash Ready bit sets...
+ */
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT,
+ MDM_STAT_FREADY | MDM_STAT_SYSRES,
+ MDM_STAT_FREADY);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM : flash ready timeout");
+ return retval;
+ }
+
+ /*
+ * ... Write the MDM-AP control register to set the Flash Mass
+ * Erase in Progress bit. This will start the mass erase
+ * process...
+ */
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL,
+ MEM_CTRL_SYS_RES_REQ | MEM_CTRL_FMEIP);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* As a sanity check make sure that device started mass erase procedure */
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT,
+ MDM_STAT_FMEACK, MDM_STAT_FMEACK);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /*
+ * ... Read the MDM-AP control register until the Flash Mass
+ * Erase in Progress bit clears...
+ */
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_CTRL,
+ MEM_CTRL_FMEIP,
+ 0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /*
+ * ... Negate the RESET signal or clear the System Reset Request
+ * bit in the MDM-AP control register...
+ */
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, 0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ dap_ap_select(dap, original_ap);
+ return ERROR_OK;
+}
+
+static const uint32_t kinetis_known_mdm_ids[] = {
+ 0x001C0020, /* KL26Z */
+};
+
+/*
+ * This function implements the procedure to connect to
+ * SWD/JTAG on Kinetis K and L series of devices as it is described in
+ * AN4835 "Production Flash Programming Best Practices for Kinetis K-
+ * and L-series MCUs" Section 4.1.1
+ */
+COMMAND_HANDLER(kinetis_check_flash_security_status)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+
+ uint32_t val;
+ int retval;
+ const uint8_t origninal_ap = dap->ap_current;
+
+ dap_ap_select(dap, 1);
+
+
+ /*
+ * ... The MDM-AP ID register can be read to verify that the
+ * connection is working correctly...
+ */
+ retval = kinetis_mdm_read_register(dap, MDM_REG_ID, &val);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to read ID register");
+ goto fail;
+ }
+
+ bool found = false;
+ for (size_t i = 0; i < ARRAY_SIZE(kinetis_known_mdm_ids); i++) {
+ if (val == kinetis_known_mdm_ids[i]) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ LOG_WARNING("MDM: unknown ID %08" PRIX32, val);
+
+ /*
+ * ... Read the MDM-AP status register until the Flash Ready bit sets...
+ */
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT,
+ MDM_STAT_FREADY,
+ MDM_STAT_FREADY);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: flash ready timeout");
+ goto fail;
+ }
+
+ /*
+ * ... Read the System Security bit to determine if security is enabled.
+ * If System Security = 0, then proceed. If System Security = 1, then
+ * communication with the internals of the processor, including the
+ * flash, will not be possible without issuing a mass erase command or
+ * unsecuring the part through other means (backdoor key unlock)...
+ */
+ retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &val);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to read MDM_REG_STAT");
+ goto fail;
+ }
+
+ if (val & MDM_STAT_SYSSEC) {
+ jtag_poll_set_enabled(false);
+
+ LOG_WARNING("*********** ATTENTION! ATTENTION! ATTENTION! ATTENTION! **********");
+ LOG_WARNING("**** ****");
+ LOG_WARNING("**** Your Kinetis MCU is in secured state, which means that, ****");
+ LOG_WARNING("**** with exeption for very basic communication, JTAG/SWD ****");
+ LOG_WARNING("**** interface will NOT work. In order to restore its ****");
+ LOG_WARNING("**** functionality please issue 'kinetis mdm mass_erase' ****");
+ LOG_WARNING("**** command, power cycle the MCU and restart openocd. ****");
+ LOG_WARNING("**** ****");
+ LOG_WARNING("*********** ATTENTION! ATTENTION! ATTENTION! ATTENTION! **********");
+ } else {
+ LOG_INFO("MDM: Chip is unsecured. Continuing.");
+ jtag_poll_set_enabled(true);
+ }
+
+ dap_ap_select(dap, origninal_ap);
+
+ return ERROR_OK;
+
+fail:
+ LOG_ERROR("MDM: Failed to check security status of the MCU. Cannot proceed further");
+ jtag_poll_set_enabled(false);
+ return retval;
+}
+
FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command)
{
struct kinetis_flash_bank *bank_info;
@@ -514,7 +776,6 @@ static int kinetis_ftfx_command(struct flash_bank *bank, uint8_t fcmd, uint32_t
static int kinetis_mass_erase(struct flash_bank *bank)
{
- int result;
uint8_t ftfx_fstat;
if (bank->target->state != TARGET_HALTED) {
@@ -522,26 +783,31 @@ static int kinetis_mass_erase(struct flash_bank *bank)
return ERROR_TARGET_NOT_HALTED;
}
- /* check if whole bank is blank */
LOG_INFO("Execute Erase All Blocks");
- /* set command and sector address */
- result = kinetis_ftfx_command(bank, FTFx_CMD_MASSERASE, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
- /* Anyway Result, write FSEC to unsecure forcely */
- /* if (result != ERROR_OK)
- return result;*/
-
- /* Write to MCU security status unsecure in Flash security byte(for Kinetis-L need) */
- LOG_INFO("Write to MCU security status unsecure Anyway!");
- uint8_t padding[4] = {0xFE, 0xFF, 0xFF, 0xFF}; /* Write 0xFFFFFFFE */
-
- result = kinetis_ftfx_command(bank, FTFx_CMD_LWORDPROG, (bank->base + 0x0000040C),
- padding[3], padding[2], padding[1], padding[0],
- 0, 0, 0, 0, &ftfx_fstat);
+ return kinetis_ftfx_command(bank, FTFx_CMD_MASSERASE, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
+}
+
+COMMAND_HANDLER(kinetis_securing_test)
+{
+ int result;
+ uint8_t ftfx_fstat;
+ struct target *target = get_current_target(CMD_CTX);
+ struct flash_bank *bank = NULL;
+
+ result = get_flash_bank_by_addr(target, 0x00000000, true, &bank);
if (result != ERROR_OK)
- return ERROR_FLASH_OPERATION_FAILED;
+ return result;
- return ERROR_OK;
+ assert(bank != NULL);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ return kinetis_ftfx_command(bank, FTFx_CMD_SECTERASE, bank->base + 0x00000400,
+ 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
}
static int kinetis_erase(struct flash_bank *bank, int first, int last)
@@ -1150,8 +1416,58 @@ static int kinetis_blank_check(struct flash_bank *bank)
return ERROR_OK;
}
+static const struct command_registration kinetis_securtiy_command_handlers[] = {
+ {
+ .name = "check_security",
+ .mode = COMMAND_EXEC,
+ .help = "",
+ .usage = "",
+ .handler = kinetis_check_flash_security_status,
+ },
+ {
+ .name = "mass_erase",
+ .mode = COMMAND_EXEC,
+ .help = "",
+ .usage = "",
+ .handler = kinetis_mdm_mass_erase,
+ },
+ {
+ .name = "test_securing",
+ .mode = COMMAND_EXEC,
+ .help = "",
+ .usage = "",
+ .handler = kinetis_securing_test,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration kinetis_exec_command_handlers[] = {
+ {
+ .name = "mdm",
+ .mode = COMMAND_ANY,
+ .help = "",
+ .usage = "",
+ .chain = kinetis_securtiy_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration kinetis_command_handler[] = {
+ {
+ .name = "kinetis",
+ .mode = COMMAND_ANY,
+ .help = "kinetis NAND flash controller commands",
+ .usage = "",
+ .chain = kinetis_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+
+
struct flash_driver kinetis_flash = {
.name = "kinetis",
+ .commands = kinetis_command_handler,
.flash_bank_command = kinetis_flash_bank_command,
.erase = kinetis_erase,
.protect = kinetis_protect,
diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c
index a1559007..1c80fcc2 100644
--- a/src/target/arm_adi_v5.c
+++ b/src/target/arm_adi_v5.c
@@ -616,186 +616,6 @@ int mem_ap_sel_write_buf_noincr(struct adiv5_dap *swjdp, uint8_t ap,
return mem_ap_write(swjdp, buffer, size, count, address, false);
}
-#define MDM_REG_STAT 0x00
-#define MDM_REG_CTRL 0x04
-#define MDM_REG_ID 0xfc
-
-#define MDM_STAT_FMEACK (1<<0)
-#define MDM_STAT_FREADY (1<<1)
-#define MDM_STAT_SYSSEC (1<<2)
-#define MDM_STAT_SYSRES (1<<3)
-#define MDM_STAT_FMEEN (1<<5)
-#define MDM_STAT_BACKDOOREN (1<<6)
-#define MDM_STAT_LPEN (1<<7)
-#define MDM_STAT_VLPEN (1<<8)
-#define MDM_STAT_LLSMODEXIT (1<<9)
-#define MDM_STAT_VLLSXMODEXIT (1<<10)
-#define MDM_STAT_CORE_HALTED (1<<16)
-#define MDM_STAT_CORE_SLEEPDEEP (1<<17)
-#define MDM_STAT_CORESLEEPING (1<<18)
-
-#define MEM_CTRL_FMEIP (1<<0)
-#define MEM_CTRL_DBG_DIS (1<<1)
-#define MEM_CTRL_DBG_REQ (1<<2)
-#define MEM_CTRL_SYS_RES_REQ (1<<3)
-#define MEM_CTRL_CORE_HOLD_RES (1<<4)
-#define MEM_CTRL_VLLSX_DBG_REQ (1<<5)
-#define MEM_CTRL_VLLSX_DBG_ACK (1<<6)
-#define MEM_CTRL_VLLSX_STAT_ACK (1<<7)
-
-#define MDM_ACCESS_TIMEOUT 3000 /* ms */
-
-/**
- *
- */
-int dap_syssec_kinetis_mdmap(struct adiv5_dap *dap)
-{
- uint32_t val;
- int retval;
- int timeout = 0;
- enum reset_types jtag_reset_config = jtag_get_reset_config();
-
- dap_ap_select(dap, 1);
-
- /* first check mdm-ap id register */
- retval = dap_queue_ap_read(dap, MDM_REG_ID, &val);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
-
- if (val != 0x001C0000) {
- LOG_DEBUG("id doesn't match %08" PRIX32 " != 0x001C0000", val);
- dap_ap_select(dap, 0);
- return ERROR_FAIL;
- }
-
- /* read and parse status register
- * it's important that the device is out of
- * reset here
- */
- while (1) {
- if (timeout++ > MDM_ACCESS_TIMEOUT) {
- LOG_DEBUG("MDMAP : flash ready timeout");
- return ERROR_FAIL;
- }
- retval = dap_queue_ap_read(dap, MDM_REG_STAT, &val);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
-
- LOG_DEBUG("MDM_REG_STAT %08" PRIX32, val);
- if (val & MDM_STAT_FREADY)
- break;
- alive_sleep(1);
- }
-
- if ((val & MDM_STAT_SYSSEC)) {
- LOG_DEBUG("MDMAP: system is secured, masserase needed");
-
- if (!(val & MDM_STAT_FMEEN))
- LOG_DEBUG("MDMAP: masserase is disabled");
- else {
- /* we need to assert reset */
- if (jtag_reset_config & RESET_HAS_SRST) {
- /* default to asserting srst */
- adapter_assert_reset();
- } else {
- LOG_DEBUG("SRST not configured");
- dap_ap_select(dap, 0);
- return ERROR_FAIL;
- }
- timeout = 0;
- while (1) {
- if (timeout++ > MDM_ACCESS_TIMEOUT) {
- LOG_DEBUG("MDMAP : flash ready timeout");
- return ERROR_FAIL;
- }
- retval = dap_queue_ap_write(dap, MDM_REG_CTRL, MEM_CTRL_FMEIP);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
- /* read status register and wait for ready */
- retval = dap_queue_ap_read(dap, MDM_REG_STAT, &val);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
- LOG_DEBUG("MDM_REG_STAT %08" PRIX32, val);
-
- if ((val & 1))
- break;
- alive_sleep(1);
- }
- timeout = 0;
- while (1) {
- if (timeout++ > MDM_ACCESS_TIMEOUT) {
- LOG_DEBUG("MDMAP : flash ready timeout");
- return ERROR_FAIL;
- }
- retval = dap_queue_ap_write(dap, MDM_REG_CTRL, 0);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
- /* read status register */
- retval = dap_queue_ap_read(dap, MDM_REG_STAT, &val);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
- LOG_DEBUG("MDM_REG_STAT %08" PRIX32, val);
- /* read control register and wait for ready */
- retval = dap_queue_ap_read(dap, MDM_REG_CTRL, &val);
- if (retval != ERROR_OK)
- return retval;
- dap_run(dap);
- LOG_DEBUG("MDM_REG_CTRL %08" PRIX32, val);
-
- if (val == 0x00)
- break;
- alive_sleep(1);
- }
- }
- }
-
- dap_ap_select(dap, 0);
-
- return ERROR_OK;
-}
-
-/** */
-struct dap_syssec_filter {
- /** */
- uint32_t idcode;
- /** */
- int (*dap_init)(struct adiv5_dap *dap);
-};
-
-/** */
-static struct dap_syssec_filter dap_syssec_filter_data[] = {
- { 0x4BA00477, dap_syssec_kinetis_mdmap }
-};
-
-/**
- *
- */
-int dap_syssec(struct adiv5_dap *dap)
-{
- unsigned int i;
- struct jtag_tap *tap;
-
- for (i = 0; i < sizeof(dap_syssec_filter_data); i++) {
- tap = dap->jtag_info->tap;
-
- while (tap != NULL) {
- if (tap->hasidcode && (dap_syssec_filter_data[i].idcode == tap->idcode)) {
- LOG_DEBUG("DAP: mdmap_init for idcode: %08" PRIx32, tap->idcode);
- dap_syssec_filter_data[i].dap_init(dap);
- }
- tap = tap->next_tap;
- }
- }
-
- return ERROR_OK;
-}
-
/*--------------------------------------------------------------------------*/
@@ -903,8 +723,6 @@ int ahbap_debugport_init(struct adiv5_dap *dap)
if (retval != ERROR_OK)
return retval;
- dap_syssec(dap);
-
/* check that we support packed transfers */
uint32_t csw, cfg;
diff --git a/tcl/target/kl25.cfg b/tcl/target/kl25.cfg
index c5dda151..2ed6b0f1 100644
--- a/tcl/target/kl25.cfg
+++ b/tcl/target/kl25.cfg
@@ -38,6 +38,14 @@ swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_CHIPNAME.cpu
+# It is important that "kinetis mdm check_security" is called for
+# 'examine-end' event and not 'eximine-start'. Calling it in 'examine-start'
+# causes "kinetis mdm check_security" to fail the first time openocd
+# calls it when it tries to connect after the CPU has been power-cycled.
+$_CHIPNAME.cpu configure -event examine-end {
+ kinetis mdm check_security
+}
+
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
set _FLASHNAME $_CHIPNAME.flash
diff --git a/tcl/target/kl25z_hla.cfg b/tcl/target/kl25z_hla.cfg
index 218a28d9..3e561f11 100644
--- a/tcl/target/kl25z_hla.cfg
+++ b/tcl/target/kl25z_hla.cfg
@@ -24,6 +24,14 @@ hla newtap $_CHIPNAME cpu -expected-id $_CPUTAPID
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME hla_target -chain-position $_TARGETNAME
+# It is important that "kinetis mdm check_security" is called for
+# 'examine-end' event and not 'eximine-start'. Calling it in 'examine-start'
+# causes "kinetis mdm check_security" to fail the first time openocd
+# calls it when it tries to connect after the CPU has been power-cycled.
+$_CHIPNAME.cpu configure -event examine-end {
+ kinetis mdm check_security
+}
+
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
flash bank pflash kinetis 0x00000000 0x20000 0 4 $_TARGETNAME