X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fflash%2Fnor%2Fkinetis.c;h=42677a385c6482bf45d71f4c6c3443cdfab75acd;hp=86785905211f2522fd703d6462f01d7aefc41487;hb=46101959a6e8325549ff7e0165d27a32f6baec34;hpb=6cadbadb3763101a495fddfedec52781a3ac6af7 diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c index 8678590521..42677a385c 100644 --- a/src/flash/nor/kinetis.c +++ b/src/flash/nor/kinetis.c @@ -35,6 +35,7 @@ #include #include #include +#include /* * 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,