From 8c256d9e59ba944a407ed661e2ea0edba2219d4b Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 17 Jan 2017 15:28:47 +1300 Subject: [PATCH 1/5] kinetis: Add KL03 devices. --- src/target/kinetis.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index f8842a3..3d8cf37 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -119,6 +119,12 @@ bool kinetis_probe(target *t) return false; } return true; + case 0x031: /* KL03 family */ + t->driver = "KL03"; + target_add_ram(t, 0x1ffffe00, 0x200); + target_add_ram(t, 0x20000000, 0x600); + kl_gen_add_flash(t, 0, 0x8000, 0x400); + return true; } return false; } From 62dedb98248eec2183a069031f2bfb481b68a6c5 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 7 Feb 2017 09:36:47 +1300 Subject: [PATCH 2/5] Add K22F --- src/target/kinetis.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index 3d8cf37..36bbefb 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -125,6 +125,13 @@ bool kinetis_probe(target *t) target_add_ram(t, 0x20000000, 0x600); kl_gen_add_flash(t, 0, 0x8000, 0x400); return true; + case 0x220: /* K22F family */ + t->driver = "K22F"; + target_add_ram(t, 0x1c000000, 0x4000000); + target_add_ram(t, 0x20000000, 0x100000); + kl_gen_add_flash(t, 0, 0x40000, 0x800); + kl_gen_add_flash(t, 0x40000, 0x40000, 0x800); + return true; } return false; } From bb93af50c31dd50a17cb822e672c209816921c0b Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Mon, 27 Mar 2017 09:05:37 +1300 Subject: [PATCH 3/5] adiv5: Debug log on failure to read ROM table. --- src/target/adiv5.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 18de968..ed30f69 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -268,6 +268,11 @@ static void adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr) cidr |= ((uint64_t)(x & 0xff)) << (i * 8); } + if (adiv5_dp_error(ap->dp)) { + DEBUG("Fault reading ID registers\n"); + return; + } + /* CIDR preamble sanity check */ if ((cidr & ~CID_CLASS_MASK) != CID_PREAMBLE) { DEBUG("0x%"PRIx32": 0x%"PRIx32" <- does not match preamble (0x%X)\n", @@ -281,6 +286,10 @@ static void adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr) if (cid_class == cidc_romtab) { /* ROM table, probe recursively */ for (int i = 0; i < 256; i++) { uint32_t entry = adiv5_mem_read32(ap, addr + i*4); + if (adiv5_dp_error(ap->dp)) { + DEBUG("Fault reading ROM table entry\n"); + } + if (entry == 0) break; From 7de304d72aedad6b03ffa2ecf3b9c12ffe432900 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Mon, 27 Mar 2017 10:51:36 +1300 Subject: [PATCH 4/5] kinetis: Add recovery mode target. --- src/target/adiv5.c | 12 ++--- src/target/kinetis.c | 110 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index ed30f69..8d86936 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -364,15 +364,6 @@ ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel) if(!tmpap.idr) /* IDR Invalid - Should we not continue here? */ return NULL; - /* Check for ARM Mem-AP */ - uint16_t mfg = (tmpap.idr >> 17) & 0x3ff; - uint8_t cls = (tmpap.idr >> 13) & 0xf; - uint8_t type = tmpap.idr & 0xf; - if (mfg != 0x23B) /* Ditch if not ARM */ - return NULL; - if ((cls != 8) || (type == 0)) /* Ditch if not Mem-AP */ - return NULL; - /* It's valid to so create a heap copy */ ap = malloc(sizeof(*ap)); memcpy(ap, &tmpap, sizeof(*ap)); @@ -447,6 +438,9 @@ void adiv5_dp_init(ADIv5_DP_t *dp) if (ap == NULL) continue; + extern void kinetis_mdm_probe(ADIv5_AP_t *); + kinetis_mdm_probe(ap); + if (ap->base == 0xffffffff) { /* No debug entries... useless AP */ adiv5_ap_unref(ap); diff --git a/src/target/kinetis.c b/src/target/kinetis.c index 36bbefb..af0326d 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -199,3 +199,113 @@ static int kl_gen_flash_write(struct target_flash *f, } return 0; } + +/*** Kinetis recovery mode using the MDM-AP ***/ + +/* Kinetis security bits are stored in regular flash, so it is possible + * to enable protection by accident when flashing a bad binary. + * a backdoor AP is provided which may allow a mass erase to recover the + * device. This provides a fake target to allow a monitor command interface + */ +#include "adiv5.h" + +#define KINETIS_MDM_IDR_K22F 0x1c0000 +#define KINETIS_MDM_IDR_KZ03 0x1c0020 + +static bool kinetis_mdm_cmd_erase_mass(target *t); + +const struct command_s kinetis_mdm_cmd_list[] = { + {"erase_mass", (cmd_handler)kinetis_mdm_cmd_erase_mass, "Erase entire flash memory"}, + {NULL, NULL, NULL} +}; + +bool nop_function(void) +{ + return true; +} + +enum target_halt_reason mdm_halt_poll(target *t, target_addr *watch) +{ + (void)t; (void)watch; + return TARGET_HALT_REQUEST; +} + +void kinetis_mdm_probe(ADIv5_AP_t *ap) +{ + switch(ap->idr) { + case KINETIS_MDM_IDR_KZ03: + case KINETIS_MDM_IDR_K22F: + break; + default: + return; + } + + target *t = target_new(); + adiv5_ap_ref(ap); + t->priv = ap; + t->priv_free = (void*)adiv5_ap_unref; + + t->driver = "Kinetis Recovery (MDM-AP)"; + t->attach = (void*)nop_function; + t->detach = (void*)nop_function; + t->check_error = (void*)nop_function; + t->mem_read = (void*)nop_function; + t->mem_write = (void*)nop_function; + t->regs_size = 4; + t->regs_read = (void*)nop_function; + t->regs_write = (void*)nop_function; + t->reset = (void*)nop_function; + t->halt_request = (void*)nop_function; + t->halt_poll = mdm_halt_poll; + t->halt_resume = (void*)nop_function; + + target_add_commands(t, kinetis_mdm_cmd_list, t->driver); +} + +#define MDM_STATUS ADIV5_AP_REG(0x00) +#define MDM_CONTROL ADIV5_AP_REG(0x04) + +#define MDM_STATUS_MASS_ERASE_ACK (1 << 0) +#define MDM_STATUS_FLASH_READY (1 << 1) +#define MDM_STATUS_MASS_ERASE_ENABLED (1 << 5) + +#define MDM_CONTROL_MASS_ERASE (1 << 0) + +static bool kinetis_mdm_cmd_erase_mass(target *t) +{ + ADIv5_AP_t *ap = t->priv; + + uint32_t status, control; + status = adiv5_ap_read(ap, MDM_STATUS); + control = adiv5_ap_read(ap, MDM_CONTROL); + tc_printf(t, "Requesting mass erase (status = 0x%"PRIx32")\n", status); + + if (!(status & MDM_STATUS_MASS_ERASE_ENABLED)) { + tc_printf(t, "ERROR: Mass erase disabled!\n"); + return false; + } + + if (!(status & MDM_STATUS_FLASH_READY)) { + tc_printf(t, "ERROR: Flash not ready!\n"); + return false; + } + + if (status & MDM_STATUS_MASS_ERASE_ACK) { + tc_printf(t, "ERROR: Mass erase already in progress!\n"); + return false; + } + + adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_MASS_ERASE); + + do { + status = adiv5_ap_read(ap, MDM_STATUS); + } while (!(status & MDM_STATUS_MASS_ERASE_ACK)); + tc_printf(t, "Mass erase acknowledged\n"); + + do { + control = adiv5_ap_read(ap, MDM_CONTROL); + } while (!(control & MDM_CONTROL_MASS_ERASE)); + tc_printf(t, "Mass erase complete\n"); + + return true; +} From 0ed793fe24f64c03775e025c6ad12973e54e869b Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Mon, 27 Mar 2017 14:01:35 +1300 Subject: [PATCH 5/5] kinetis: Clobber security byte with unsecure value. May be overridden with `mon unsafe enable`. --- src/target/kinetis.c | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index af0326d..f71cec2 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -61,6 +61,24 @@ #define KL_GEN_PAGESIZE 0x400 +static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]); +static bool unsafe_enabled; + +const struct command_s kinetis_cmd_list[] = { + {"unsafe", (cmd_handler)kinetis_cmd_unsafe, "Allow programming security byte (enable|disable)"}, + {NULL, NULL, NULL} +}; + +static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]) +{ + if (argc == 1) + tc_printf(t, "Allow programming security byte: %s\n", + unsafe_enabled ? "enabled" : "disabled"); + else + unsafe_enabled = argv[1][0] == 'e'; + return true; +} + static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len); static int kl_gen_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); @@ -88,13 +106,13 @@ bool kinetis_probe(target *t) target_add_ram(t, 0x1ffff000, 0x1000); target_add_ram(t, 0x20000000, 0x3000); kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400); - return true; + break; case 0x231: t->driver = "KL27"; target_add_ram(t, 0x1fffe000, 0x2000); target_add_ram(t, 0x20000000, 0x6000); kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400); - return true; + break; case 0x021: /* KL02 family */ switch((sdid>>16) & 0x0f){ case 3: @@ -118,22 +136,26 @@ bool kinetis_probe(target *t) default: return false; } - return true; + break; case 0x031: /* KL03 family */ t->driver = "KL03"; target_add_ram(t, 0x1ffffe00, 0x200); target_add_ram(t, 0x20000000, 0x600); kl_gen_add_flash(t, 0, 0x8000, 0x400); - return true; + break; case 0x220: /* K22F family */ t->driver = "K22F"; target_add_ram(t, 0x1c000000, 0x4000000); target_add_ram(t, 0x20000000, 0x100000); kl_gen_add_flash(t, 0, 0x40000, 0x800); kl_gen_add_flash(t, 0x40000, 0x40000, 0x800); - return true; + break; + default: + return false; } - return false; + unsafe_enabled = false; + target_add_commands(t, kinetis_cmd_list, t->driver); + return true; } static bool @@ -185,9 +207,20 @@ static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t l return 0; } +#define FLASH_SECURITY_BYTE_ADDRESS 0x40C +#define FLASH_SECURITY_BYTE_UNSECURED 0xFE + static int kl_gen_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) { + /* Ensure we don't write something horrible over the security byte */ + if (!unsafe_enabled && + (dest <= FLASH_SECURITY_BYTE_ADDRESS) && + ((dest + len) > FLASH_SECURITY_BYTE_ADDRESS)) { + ((uint8_t*)src)[FLASH_SECURITY_BYTE_ADDRESS - dest] = + FLASH_SECURITY_BYTE_UNSECURED; + } + while (len) { if (kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, dest, src)) { len -= 4;