#include "jtag/interface.h"
#include "imp.h"
#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
#include <target/target_type.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
*/
/* Addressess */
+#define FCF_ADDRESS 0x00000400
+#define FCF_FPROT 0x8
+#define FCF_FSEC 0xc
+#define FCF_FOPT 0xd
+#define FCF_FDPROT 0xf
+#define FCF_SIZE 0x10
+
#define FLEXRAM 0x14000000
#define FMC_PFB01CR 0x4001f004
#define MDM_CTRL_VLLSX_DBG_ACK (1<<6)
#define MDM_CTRL_VLLSX_STAT_ACK (1<<7)
-#define MDM_ACCESS_TIMEOUT 3000 /* iterations */
+#define MDM_ACCESS_TIMEOUT 500 /* msec */
+
+
+static bool allow_fcf_writes;
+static uint8_t fcf_fopt = 0xff;
+
+
+struct flash_driver kinetis_flash;
+static int kinetis_write_inner(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count);
+static int kinetis_auto_probe(struct flash_bank *bank);
+
static int kinetis_mdm_write_register(struct adiv5_dap *dap, unsigned reg, uint32_t value)
{
return ERROR_OK;
}
-static int kinetis_mdm_poll_register(struct adiv5_dap *dap, unsigned reg, uint32_t mask, uint32_t value)
+static int kinetis_mdm_poll_register(struct adiv5_dap *dap, unsigned reg,
+ uint32_t mask, uint32_t value, uint32_t timeout_ms)
{
uint32_t val;
int retval;
- int timeout = MDM_ACCESS_TIMEOUT;
+ int64_t ms_timeout = timeval_ms() + timeout_ms;
do {
retval = kinetis_mdm_read_register(dap, reg, &val);
return retval;
alive_sleep(1);
- } while (timeout--);
+ } while (timeval_ms() < ms_timeout);
LOG_DEBUG("MDM: polling timed out");
return ERROR_FAIL;
int retval;
int tries = 0;
uint32_t stat;
+ int64_t ms_timeout = timeval_ms() + MDM_ACCESS_TIMEOUT;
if (!dap) {
LOG_ERROR("Cannot perform halt with a high-level adapter");
== (MDM_STAT_FREADY | MDM_STAT_SYSRES))
break;
- if (tries > MDM_ACCESS_TIMEOUT) {
+ if (timeval_ms() >= ms_timeout) {
LOG_ERROR("MDM: halt timed out");
return ERROR_FAIL;
}
return retval;
}
- retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT, MDM_STAT_SYSRES, 0);
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT, MDM_STAT_SYSRES, 0, 500);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: failed to assert reset");
return retval;
}
/*
- * ... Read the MDM-AP status register Mass Erase Enable bit to
- * determine if the mass erase command is enabled. If Mass Erase
- * Enable = 0, then mass erase is disabled and the processor
- * cannot be erased or unsecured. If Mass Erase Enable = 1, then
- * the mass erase command can be used...
+ * ... Read the MDM-AP status register repeatedly and wait for
+ * stable conditions suitable for mass erase:
+ * - mass erase is enabled
+ * - flash is ready
+ * - reset is finished
+ *
+ * Mass erase is started as soon as all conditions are met in 32
+ * subsequent status reads.
+ *
+ * In case of not stable conditions (RESET/WDOG loop in secured device)
+ * the user is asked for manual pressing of RESET button
+ * as a last resort.
*/
- uint32_t stat;
+ int cnt_mass_erase_disabled = 0;
+ int cnt_ready = 0;
+ int64_t ms_start = timeval_ms();
+ bool man_reset_requested = false;
- retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &stat);
- if (retval != ERROR_OK) {
- LOG_ERROR("MDM: failed to read MDM_REG_STAT");
- goto deassert_reset_and_exit;
- }
+ do {
+ uint32_t stat = 0;
+ int64_t ms_elapsed = timeval_ms() - ms_start;
- if (!(stat & MDM_STAT_FMEEN)) {
- LOG_ERROR("MDM: mass erase is disabled");
- goto deassert_reset_and_exit;
- }
+ if (!man_reset_requested && ms_elapsed > 100) {
+ LOG_INFO("MDM: Press RESET button now if possible.");
+ man_reset_requested = true;
+ }
- if ((stat & MDM_STAT_SYSSEC) && !(jtag_get_reset_config() & RESET_HAS_SRST)) {
- LOG_ERROR("Mass erase of a secured MCU is not possible without hardware reset.");
- LOG_INFO("Connect SRST and use 'reset_config srst_only'.");
- goto deassert_reset_and_exit;
- }
+ if (ms_elapsed > 3000) {
+ LOG_ERROR("MDM: waiting for mass erase conditions timed out.");
+ LOG_INFO("Mass erase of a secured MCU is not possible without hardware reset.");
+ LOG_INFO("Connect SRST, use 'reset_config srst_only' and retry.");
+ goto deassert_reset_and_exit;
+ }
+ retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &stat);
+ if (retval != ERROR_OK) {
+ cnt_ready = 0;
+ continue;
+ }
- /*
- * ... Read the MDM-AP status register until the Flash Ready bit sets
- * and System Reset is asserted...
- */
- 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 / system reset timeout");
- goto deassert_reset_and_exit;
- }
+ if (!(stat & MDM_STAT_FMEEN)) {
+ cnt_ready = 0;
+ cnt_mass_erase_disabled++;
+ if (cnt_mass_erase_disabled > 10) {
+ LOG_ERROR("MDM: mass erase is disabled");
+ goto deassert_reset_and_exit;
+ }
+ continue;
+ }
+
+ if ((stat & (MDM_STAT_FREADY | MDM_STAT_SYSRES)) == MDM_STAT_FREADY)
+ cnt_ready++;
+ else
+ cnt_ready = 0;
+
+ } while (cnt_ready < 32);
/*
* ... Write the MDM-AP control register to set the Flash Mass
/*
* ... Read the MDM-AP control register until the Flash Mass
* Erase in Progress bit clears...
+ * Data sheed defines erase time <3.6 sec/512kB flash block.
+ * The biggest device has 4 pflash blocks => timeout 16 sec.
*/
- retval = kinetis_mdm_poll_register(dap, MDM_REG_CTRL, MDM_CTRL_FMEIP, 0);
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_CTRL, MDM_CTRL_FMEIP, 0, 16000);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: mass erase timeout");
goto deassert_reset_and_exit;
}
+static int kinetis_ftfx_decode_error(uint8_t fstat)
+{
+ if (fstat & 0x20) {
+ LOG_ERROR("Flash operation failed, illegal command");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+
+ } else if (fstat & 0x10)
+ LOG_ERROR("Flash operation failed, protection violated");
+
+ else if (fstat & 0x40)
+ LOG_ERROR("Flash operation failed, read collision");
+
+ else if (fstat & 0x80)
+ return ERROR_OK;
+
+ else
+ LOG_ERROR("Flash operation timed out");
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+
+static int kinetis_ftfx_prepare(struct target *target)
+{
+ int result, i;
+ uint8_t fstat;
+
+ /* wait until busy */
+ for (i = 0; i < 50; i++) {
+ result = target_read_u8(target, FTFx_FSTAT, &fstat);
+ if (result != ERROR_OK)
+ return result;
+
+ if (fstat & 0x80)
+ break;
+ }
+
+ if ((fstat & 0x80) == 0) {
+ LOG_ERROR("Flash controller is busy");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ if (fstat != 0x80) {
+ /* reset error flags */
+ result = target_write_u8(target, FTFx_FSTAT, 0x70);
+ }
+ return result;
+}
+
/* Kinetis Program-LongWord Microcodes */
static const uint8_t kinetis_flash_write_code[] = {
/* Params:
if (buffer_size < (target->working_area_size/2))
buffer_size = (target->working_area_size/2);
- LOG_INFO("Kinetis: FLASH Write ...");
-
- /* check code alignment */
- if (offset & 0x1) {
- LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
- }
-
/* allocate working area with flash programming code */
if (target_alloc_working_area(target, sizeof(kinetis_flash_write_code),
&write_algorithm) != ERROR_OK) {
static int kinetis_protect(struct flash_bank *bank, int set, int first, int last)
{
- LOG_WARNING("kinetis_protect not supported yet");
- /* FIXME: TODO */
+ int i;
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
+ if (allow_fcf_writes) {
+ LOG_ERROR("Protection setting is possible with 'kinetis fcf_source protection' only!");
+ return ERROR_FAIL;
+ }
+
+ if (!bank->prot_blocks || bank->num_prot_blocks == 0) {
+ LOG_ERROR("No protection possible for current bank!");
+ return ERROR_FLASH_BANK_INVALID;
}
- return ERROR_FLASH_BANK_INVALID;
+ for (i = first; i < bank->num_prot_blocks && i <= last; i++)
+ bank->prot_blocks[i].is_protected = set;
+
+ LOG_INFO("Protection bits will be written at the next FCF sector erase or write.");
+ LOG_INFO("Do not issue 'flash info' command until protection is written,");
+ LOG_INFO("doing so would re-read protection status from MCU.");
+
+ return ERROR_OK;
}
static int kinetis_protect_check(struct flash_bank *bank)
struct kinetis_flash_bank *kinfo = bank->driver_priv;
int result;
int i, b;
- uint32_t fprot, psec;
-
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
+ uint32_t fprot;
if (kinfo->flash_class == FC_PFLASH) {
- uint8_t buffer[4];
/* read protection register */
- result = target_read_memory(bank->target, FTFx_FPROT3, 1, 4, buffer);
-
+ result = target_read_u32(bank->target, FTFx_FPROT3, &fprot);
if (result != ERROR_OK)
return result;
- fprot = target_buffer_get_u32(bank->target, buffer);
/* Every bit protects 1/32 of the full flash (not necessarily just this bank) */
} else if (kinfo->flash_class == FC_FLEX_NVM) {
uint8_t fdprot;
/* read protection register */
- result = target_read_memory(bank->target, FTFx_FDPROT, 1, 1, &fdprot);
-
+ result = target_read_u8(bank->target, FTFx_FDPROT, &fdprot);
if (result != ERROR_OK)
return result;
}
b = kinfo->protection_block;
- for (psec = 0, i = 0; i < bank->num_sectors; i++) {
+ for (i = 0; i < bank->num_prot_blocks; i++) {
if ((fprot >> b) & 1)
- bank->sectors[i].is_protected = 0;
+ bank->prot_blocks[i].is_protected = 0;
else
- bank->sectors[i].is_protected = 1;
+ bank->prot_blocks[i].is_protected = 1;
- psec += bank->sectors[i].size;
+ b++;
+ }
+
+ return ERROR_OK;
+}
+
+
+static int kinetis_fill_fcf(struct flash_bank *bank, uint8_t *fcf)
+{
+ uint32_t fprot = 0xffffffff;
+ uint8_t fsec = 0xfe; /* set MCU unsecure */
+ uint8_t fdprot = 0xff;
+ int i;
+ uint32_t pflash_bit;
+ uint8_t dflash_bit;
+ struct flash_bank *bank_iter;
+ struct kinetis_flash_bank *kinfo;
+
+ memset(fcf, 0xff, FCF_SIZE);
+
+ pflash_bit = 1;
+ dflash_bit = 1;
+
+ /* iterate over all kinetis banks */
+ /* current bank is bank 0, it contains FCF */
+ for (bank_iter = bank; bank_iter; bank_iter = bank_iter->next) {
+ if (bank_iter->driver != &kinetis_flash
+ || bank_iter->target != bank->target)
+ continue;
+
+ kinetis_auto_probe(bank_iter);
+
+ kinfo = bank->driver_priv;
+ if (!kinfo)
+ continue;
+
+ if (kinfo->flash_class == FC_PFLASH) {
+ for (i = 0; i < bank_iter->num_prot_blocks; i++) {
+ if (bank_iter->prot_blocks[i].is_protected == 1)
+ fprot &= ~pflash_bit;
+
+ pflash_bit <<= 1;
+ }
+
+ } else if (kinfo->flash_class == FC_FLEX_NVM) {
+ for (i = 0; i < bank_iter->num_prot_blocks; i++) {
+ if (bank_iter->prot_blocks[i].is_protected == 1)
+ fdprot &= ~dflash_bit;
+
+ dflash_bit <<= 1;
+ }
- if (psec >= kinfo->protection_size) {
- psec = 0;
- b++;
}
}
+ target_buffer_set_u32(bank->target, fcf + FCF_FPROT, fprot);
+ fcf[FCF_FSEC] = fsec;
+ fcf[FCF_FOPT] = fcf_fopt;
+ fcf[FCF_FDPROT] = fdprot;
return ERROR_OK;
}
uint8_t command[12] = {faddr & 0xff, (faddr >> 8) & 0xff, (faddr >> 16) & 0xff, fcmd,
fccob7, fccob6, fccob5, fccob4,
fccobb, fccoba, fccob9, fccob8};
- int result, i;
- uint8_t buffer;
-
- /* wait for done */
- for (i = 0; i < 50; i++) {
- result =
- target_read_memory(target, FTFx_FSTAT, 1, 1, &buffer);
-
- if (result != ERROR_OK)
- return result;
-
- if (buffer & 0x80)
- break;
-
- buffer = 0x00;
- }
-
- if (buffer != 0x80) {
- /* reset error flags */
- buffer = 0x30;
- result =
- target_write_memory(target, FTFx_FSTAT, 1, 1, &buffer);
- if (result != ERROR_OK)
- return result;
- }
+ int result;
+ uint8_t fstat;
+ int64_t ms_timeout = timeval_ms() + 250;
result = target_write_memory(target, FTFx_FCCOB3, 4, 3, command);
-
if (result != ERROR_OK)
return result;
/* start command */
- buffer = 0x80;
- result = target_write_memory(target, FTFx_FSTAT, 1, 1, &buffer);
+ result = target_write_u8(target, FTFx_FSTAT, 0x80);
if (result != ERROR_OK)
return result;
/* wait for done */
- for (i = 0; i < 240; i++) { /* Need longtime for "Mass Erase" Command Nemui Changed */
- result =
- target_read_memory(target, FTFx_FSTAT, 1, 1, ftfx_fstat);
+ do {
+ result = target_read_u8(target, FTFx_FSTAT, &fstat);
if (result != ERROR_OK)
return result;
- if (*ftfx_fstat & 0x80)
+ if (fstat & 0x80)
break;
- }
- if ((*ftfx_fstat & 0xf0) != 0x80) {
- LOG_ERROR
- ("ftfx command failed FSTAT: %02X FCCOB: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
- *ftfx_fstat, command[3], command[2], command[1], command[0],
+ } while (timeval_ms() < ms_timeout);
+
+ if (ftfx_fstat)
+ *ftfx_fstat = fstat;
+
+ if ((fstat & 0xf0) != 0x80) {
+ LOG_DEBUG("ftfx command failed FSTAT: %02X FCCOB: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
+ fstat, command[3], command[2], command[1], command[0],
command[7], command[6], command[5], command[4],
command[11], command[10], command[9], command[8]);
- return ERROR_FLASH_OPERATION_FAILED;
+
+ return kinetis_ftfx_decode_error(fstat);
}
return ERROR_OK;
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
if ((first > bank->num_sectors) || (last > bank->num_sectors))
return ERROR_FLASH_OPERATION_FAILED;
* block. Should be quicker.
*/
for (i = first; i <= last; i++) {
- uint8_t ftfx_fstat;
/* set command and sector address */
result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTERASE, kinfo->prog_base + bank->sectors[i].offset,
- 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, 0, 0, 0, 0, NULL);
if (result != ERROR_OK) {
LOG_WARNING("erase sector %d failed", i);
}
bank->sectors[i].is_erased = 1;
+
+ if (bank->base == 0
+ && bank->sectors[i].offset <= FCF_ADDRESS
+ && bank->sectors[i].offset + bank->sectors[i].size > FCF_ADDRESS + FCF_SIZE) {
+ if (allow_fcf_writes) {
+ LOG_WARNING("Flash Configuration Field erased, DO NOT reset or power off the device");
+ LOG_WARNING("until correct FCF is programmed or MCU gets security lock.");
+ } else {
+ uint8_t fcf_buffer[FCF_SIZE];
+
+ kinetis_fill_fcf(bank, fcf_buffer);
+ result = kinetis_write_inner(bank, fcf_buffer, FCF_ADDRESS, FCF_SIZE);
+ if (result != ERROR_OK)
+ LOG_WARNING("Flash Configuration Field write failed");
+ bank->sectors[i].is_erased = 0;
+ }
+ }
}
kinetis_invalidate_flash_cache(bank);
- if (first == 0) {
- LOG_WARNING
- ("flash configuration field erased, please reset the device");
- }
-
return ERROR_OK;
}
static int kinetis_make_ram_ready(struct target *target)
{
int result;
- uint8_t ftfx_fstat;
uint8_t ftfx_fcnfg;
/* check if ram ready */
- result = target_read_memory(target, FTFx_FCNFG, 1, 1, &ftfx_fcnfg);
+ result = target_read_u8(target, FTFx_FCNFG, &ftfx_fcnfg);
if (result != ERROR_OK)
return result;
/* make flex ram available */
result = kinetis_ftfx_command(target, FTFx_CMD_SETFLEXRAM, 0x00ff0000,
- 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, 0, 0, 0, 0, NULL);
if (result != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
/* check again */
- result = target_read_memory(target, FTFx_FCNFG, 1, 1, &ftfx_fcnfg);
+ result = target_read_u8(target, FTFx_FCNFG, &ftfx_fcnfg);
if (result != ERROR_OK)
return result;
return ERROR_FLASH_OPERATION_FAILED;
}
-static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
+
+static int kinetis_write_sections(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
- unsigned int i, result, fallback = 0;
- uint32_t wc;
+ int result;
struct kinetis_flash_bank *kinfo = bank->driver_priv;
- uint8_t *new_buffer = NULL;
+ uint8_t *buffer_aligned = NULL;
+ /*
+ * Kinetis uses different terms for the granularity of
+ * sector writes, e.g. "phrase" or "128 bits". We use
+ * the generic term "chunk". The largest possible
+ * Kinetis "chunk" is 16 bytes (128 bits).
+ */
+ uint32_t prog_section_chunk_bytes = kinfo->sector_size >> 8;
+ uint32_t prog_size_bytes = kinfo->max_flash_prog_size;
+
+ while (count > 0) {
+ uint32_t size = prog_size_bytes - offset % prog_size_bytes;
+ uint32_t align_begin = offset % prog_section_chunk_bytes;
+ uint32_t align_end;
+ uint32_t size_aligned;
+ uint16_t chunk_count;
+ uint8_t ftfx_fstat;
- result = kinetis_check_run_mode(bank->target);
- if (result != ERROR_OK)
- return result;
+ if (size > count)
+ size = count;
+
+ align_end = (align_begin + size) % prog_section_chunk_bytes;
+ if (align_end)
+ align_end = prog_section_chunk_bytes - align_end;
+
+ size_aligned = align_begin + size + align_end;
+ chunk_count = size_aligned / prog_section_chunk_bytes;
+
+ if (size != size_aligned) {
+ /* aligned section: the first, the last or the only */
+ if (!buffer_aligned)
+ buffer_aligned = malloc(prog_size_bytes);
+
+ memset(buffer_aligned, 0xff, size_aligned);
+ memcpy(buffer_aligned + align_begin, buffer, size);
+
+ result = target_write_memory(bank->target, FLEXRAM,
+ 4, size_aligned / 4, buffer_aligned);
+
+ LOG_DEBUG("section @ %08" PRIx32 " aligned begin %" PRIu32 ", end %" PRIu32,
+ bank->base + offset, align_begin, align_end);
+ } else
+ result = target_write_memory(bank->target, FLEXRAM,
+ 4, size_aligned / 4, buffer);
+
+ LOG_DEBUG("write section @ %08" PRIx32 " with length %" PRIu32 " bytes",
+ bank->base + offset, size);
+
+ if (result != ERROR_OK) {
+ LOG_ERROR("target_write_memory failed");
+ break;
+ }
+
+ /* execute section-write command */
+ result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTWRITE,
+ kinfo->prog_base + offset - align_begin,
+ chunk_count>>8, chunk_count, 0, 0,
+ 0, 0, 0, 0, &ftfx_fstat);
+
+ if (result != ERROR_OK) {
+ LOG_ERROR("Error writing section at %08" PRIx32, bank->base + offset);
+ break;
+ }
+
+ if (ftfx_fstat & 0x01)
+ LOG_ERROR("Flash write error at %08" PRIx32, bank->base + offset);
+
+ buffer += size;
+ offset += size;
+ count -= size;
+ }
+
+ free(buffer_aligned);
+ return result;
+}
+
+
+static int kinetis_write_inner(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ int result, fallback = 0;
+ struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (!(kinfo->flash_support & FS_PROGRAM_SECTOR)) {
/* fallback to longword write */
}
}
- LOG_DEBUG("flash write @08%" PRIX32, offset);
-
+ LOG_DEBUG("flash write @08%" PRIx32, bank->base + offset);
- /* program section command */
if (fallback == 0) {
- /*
- * Kinetis uses different terms for the granularity of
- * sector writes, e.g. "phrase" or "128 bits". We use
- * the generic term "chunk". The largest possible
- * Kinetis "chunk" is 16 bytes (128 bits).
- */
- unsigned prog_section_chunk_bytes = kinfo->sector_size >> 8;
- unsigned prog_size_bytes = kinfo->max_flash_prog_size;
- for (i = 0; i < count; i += prog_size_bytes) {
- uint8_t residual_buffer[16];
- uint8_t ftfx_fstat;
- uint32_t section_count = prog_size_bytes / prog_section_chunk_bytes;
- uint32_t residual_wc = 0;
-
- /*
- * Assume the word count covers an entire
- * sector.
- */
- wc = prog_size_bytes / 4;
-
- /*
- * If bytes to be programmed are less than the
- * full sector, then determine the number of
- * full-words to program, and put together the
- * residual buffer so that a full "section"
- * may always be programmed.
- */
- if ((count - i) < prog_size_bytes) {
- /* number of bytes to program beyond full section */
- unsigned residual_bc = (count-i) % prog_section_chunk_bytes;
-
- /* number of complete words to copy directly from buffer */
- wc = (count - i - residual_bc) / 4;
-
- /* number of total sections to write, including residual */
- section_count = DIV_ROUND_UP((count-i), prog_section_chunk_bytes);
-
- /* any residual bytes delivers a whole residual section */
- residual_wc = (residual_bc ? prog_section_chunk_bytes : 0)/4;
-
- /* clear residual buffer then populate residual bytes */
- (void) memset(residual_buffer, 0xff, prog_section_chunk_bytes);
- (void) memcpy(residual_buffer, &buffer[i+4*wc], residual_bc);
- }
-
- LOG_DEBUG("write section @ %08" PRIX32 " with length %" PRIu32 " bytes",
- offset + i, (uint32_t)wc*4);
-
- /* write data to flexram as whole-words */
- result = target_write_memory(bank->target, FLEXRAM, 4, wc,
- buffer + i);
-
- if (result != ERROR_OK) {
- LOG_ERROR("target_write_memory failed");
- return result;
- }
-
- /* write the residual words to the flexram */
- if (residual_wc) {
- result = target_write_memory(bank->target,
- FLEXRAM+4*wc,
- 4, residual_wc,
- residual_buffer);
-
- if (result != ERROR_OK) {
- LOG_ERROR("target_write_memory failed");
- return result;
- }
- }
-
- /* execute section-write command */
- result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTWRITE, kinfo->prog_base + offset + i,
- section_count>>8, section_count, 0, 0,
- 0, 0, 0, 0, &ftfx_fstat);
-
- if (result != ERROR_OK)
- return ERROR_FLASH_OPERATION_FAILED;
- }
+ /* program section command */
+ kinetis_write_sections(bank, buffer, offset, count);
}
- /* program longword command, not supported in "SF3" devices */
else if (kinfo->flash_support & FS_PROGRAM_LONGWORD) {
+ /* program longword command, not supported in FTFE */
+ uint8_t *new_buffer = NULL;
+
+ /* check word alignment */
+ if (offset & 0x3) {
+ LOG_ERROR("offset 0x%" PRIx32 " breaks the required alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
if (count & 0x3) {
uint32_t old_count = count;
count = (old_count | 3) + 1;
}
LOG_INFO("odd number of bytes to write (%" PRIu32 "), extending to %" PRIu32 " "
"and padding with 0xff", old_count, count);
- memset(new_buffer, 0xff, count);
+ memset(new_buffer + old_count, 0xff, count - old_count);
buffer = memcpy(new_buffer, buffer, old_count);
}
kinetis_disable_wdog(bank->target, kinfo->sim_sdid);
/* try using a block write */
- int retval = kinetis_write_block(bank, buffer, offset, words_remaining);
+ result = kinetis_write_block(bank, buffer, offset, words_remaining);
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+ if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single word accesses */
LOG_WARNING("couldn't use block writes, falling back to single "
"memory accesses");
- for (i = 0; i < count; i += 4) {
+ while (words_remaining) {
uint8_t ftfx_fstat;
- LOG_DEBUG("write longword @ %08" PRIX32, (uint32_t)(offset + i));
-
- uint8_t padding[4] = {0xff, 0xff, 0xff, 0xff};
- memcpy(padding, buffer + i, MIN(4, count-i));
+ LOG_DEBUG("write longword @ %08" PRIx32, (uint32_t)(bank->base + offset));
- result = kinetis_ftfx_command(bank->target, FTFx_CMD_LWORDPROG, kinfo->prog_base + offset + i,
- padding[3], padding[2], padding[1], padding[0],
+ result = kinetis_ftfx_command(bank->target, FTFx_CMD_LWORDPROG, kinfo->prog_base + offset,
+ buffer[3], buffer[2], buffer[1], buffer[0],
0, 0, 0, 0, &ftfx_fstat);
- if (result != ERROR_OK)
- return ERROR_FLASH_OPERATION_FAILED;
+ if (result != ERROR_OK) {
+ LOG_ERROR("Error writing longword at %08" PRIx32, bank->base + offset);
+ break;
+ }
+
+ if (ftfx_fstat & 0x01)
+ LOG_ERROR("Flash write error at %08" PRIx32, bank->base + offset);
+
+ buffer += 4;
+ offset += 4;
+ words_remaining--;
}
}
+ free(new_buffer);
} else {
LOG_ERROR("Flash write strategy not implemented");
return ERROR_FLASH_OPERATION_FAILED;
}
kinetis_invalidate_flash_cache(bank);
- return ERROR_OK;
+ return result;
}
+
+static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ int result;
+ bool set_fcf = false;
+ int sect = 0;
+
+ result = kinetis_check_run_mode(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
+ if (bank->base == 0 && !allow_fcf_writes) {
+ if (bank->sectors[1].offset <= FCF_ADDRESS)
+ sect = 1; /* 1kb sector, FCF in 2nd sector */
+
+ if (offset < bank->sectors[sect].offset + bank->sectors[sect].size
+ && offset + count > bank->sectors[sect].offset)
+ set_fcf = true; /* write to any part of sector with FCF */
+ }
+
+ if (set_fcf) {
+ uint8_t fcf_buffer[FCF_SIZE];
+ uint8_t fcf_current[FCF_SIZE];
+
+ kinetis_fill_fcf(bank, fcf_buffer);
+
+ if (offset < FCF_ADDRESS) {
+ /* write part preceding FCF */
+ result = kinetis_write_inner(bank, buffer, offset, FCF_ADDRESS - offset);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ result = target_read_memory(bank->target, FCF_ADDRESS, 4, FCF_SIZE / 4, fcf_current);
+ if (result == ERROR_OK && memcmp(fcf_current, fcf_buffer, FCF_SIZE) == 0)
+ set_fcf = false;
+
+ if (set_fcf) {
+ /* write FCF if differs from flash - eliminate multiple writes */
+ result = kinetis_write_inner(bank, fcf_buffer, FCF_ADDRESS, FCF_SIZE);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ LOG_WARNING("Flash Configuration Field written.");
+ LOG_WARNING("Reset or power off the device to make settings effective.");
+
+ if (offset + count > FCF_ADDRESS + FCF_SIZE) {
+ uint32_t delta = FCF_ADDRESS + FCF_SIZE - offset;
+ /* write part after FCF */
+ result = kinetis_write_inner(bank, buffer + delta, FCF_ADDRESS + FCF_SIZE, count - delta);
+ }
+ return result;
+
+ } else
+ /* no FCF fiddling, normal write */
+ return kinetis_write_inner(bank, buffer, offset, count);
+}
+
+
static int kinetis_probe(struct flash_bank *bank)
{
int result, i;
- uint32_t offset = 0;
uint8_t fcfg1_nvmsize, fcfg1_pfsize, fcfg1_eesize, fcfg1_depart;
uint8_t fcfg2_maxaddr0, fcfg2_pflsh, fcfg2_maxaddr1;
uint32_t nvm_size = 0, pf_size = 0, df_size = 0, ee_size = 0;
* parts with more than 32K of PFlash. For parts with
* less the protection unit is set to 1024 bytes */
kinfo->protection_size = MAX(pf_size / 32, 1024);
- kinfo->protection_block = (32 / num_pflash_blocks) * bank->bank_number;
+ bank->num_prot_blocks = 32 / num_pflash_blocks;
+ kinfo->protection_block = bank->num_prot_blocks * bank->bank_number;
} else if ((unsigned)bank->bank_number < num_blocks) {
/* nvm, banks start at address 0x10000000 */
else
kinfo->protection_size = nvm_size / 8; /* TODO: verify on SF1, not documented in RM */
}
- kinfo->protection_block = (8 / num_nvm_blocks) * nvm_ord;
+ bank->num_prot_blocks = 8 / num_nvm_blocks;
+ kinfo->protection_block = bank->num_prot_blocks * nvm_ord;
/* EEPROM backup part of FlexNVM is not accessible, use df_size as a limit */
if (df_size > bank->size * nvm_ord)
free(bank->sectors);
bank->sectors = NULL;
}
+ if (bank->prot_blocks) {
+ free(bank->prot_blocks);
+ bank->prot_blocks = NULL;
+ }
if (kinfo->sector_size == 0) {
LOG_ERROR("Unknown sector size for bank %d", bank->bank_number);
if (bank->num_sectors > 0) {
/* FlexNVM bank can be used for EEPROM backup therefore zero sized */
- bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
-
- for (i = 0; i < bank->num_sectors; i++) {
- bank->sectors[i].offset = offset;
- bank->sectors[i].size = kinfo->sector_size;
- offset += kinfo->sector_size;
- bank->sectors[i].is_erased = -1;
- bank->sectors[i].is_protected = 1;
- }
+ bank->sectors = alloc_block_array(0, kinfo->sector_size, bank->num_sectors);
+ if (!bank->sectors)
+ return ERROR_FAIL;
+
+ bank->prot_blocks = alloc_block_array(0, kinfo->protection_size, bank->num_prot_blocks);
+ if (!bank->prot_blocks)
+ return ERROR_FAIL;
+
+ } else {
+ bank->num_prot_blocks = 0;
}
kinfo->probed = true;
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
if (kinfo->flash_class == FC_PFLASH || kinfo->flash_class == FC_FLEX_NVM) {
bool block_dirty = false;
uint8_t ftfx_fstat;
unsigned long par, log2 = 0, ee1 = 0, ee2 = 0;
enum { SHOW_INFO, DF_SIZE, EEBKP_SIZE } sz_type = SHOW_INFO;
bool enable;
- uint8_t ftfx_fstat;
uint8_t load_flex_ram = 1;
uint8_t ee_size_code = 0x3f;
uint8_t flex_nvm_partition_code = 0;
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(target);
+ if (result != ERROR_OK)
+ return result;
+
result = kinetis_ftfx_command(target, FTFx_CMD_PGMPART, load_flex_ram,
ee_size_code, flex_nvm_partition_code, 0, 0,
- 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, NULL);
if (result != ERROR_OK)
return result;
return ERROR_OK;
}
+COMMAND_HANDLER(kinetis_fcf_source_handler)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1) {
+ if (strcmp(CMD_ARGV[0], "write") == 0)
+ allow_fcf_writes = true;
+ else if (strcmp(CMD_ARGV[0], "protection") == 0)
+ allow_fcf_writes = false;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (allow_fcf_writes) {
+ command_print(CMD_CTX, "Arbitrary Flash Configuration Field writes enabled.");
+ command_print(CMD_CTX, "Protection info writes to FCF disabled.");
+ LOG_WARNING("BEWARE: incorrect flash configuration may permanently lock the device.");
+ } else {
+ command_print(CMD_CTX, "Protection info writes to Flash Configuration Field enabled.");
+ command_print(CMD_CTX, "Arbitrary FCF writes disabled. Mode safe from unwanted locking of the device.");
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(kinetis_fopt_handler)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ fcf_fopt = (uint8_t)strtoul(CMD_ARGV[0], NULL, 0);
+ else
+ command_print(CMD_CTX, "FCF_FOPT 0x%02" PRIx8, fcf_fopt);
+
+ return ERROR_OK;
+}
+
static const struct command_registration kinetis_security_command_handlers[] = {
{
.usage = "('info'|'dataflash' size|'eebkp' size) [eesize1 eesize2] ['on'|'off']",
.handler = kinetis_nvm_partition,
},
+ {
+ .name = "fcf_source",
+ .mode = COMMAND_EXEC,
+ .help = "Use protection as a source for Flash Configuration Field or allow writing arbitrary values to the FCF"
+ " Mode 'protection' is safe from unwanted locking of the device.",
+ .usage = "['protection'|'write']",
+ .handler = kinetis_fcf_source_handler,
+ },
+ {
+ .name = "fopt",
+ .mode = COMMAND_EXEC,
+ .help = "FCF_FOPT value source in 'kinetis fcf_source protection' mode",
+ .usage = "[num]",
+ .handler = kinetis_fopt_handler,
+ },
COMMAND_REGISTRATION_DONE
};