/* * This file is part of the Black Magic Debug project. * * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* This file implements STM32 target specific functions for detecting * the device, providing the XML memory map and Flash memory programming. * * Refereces: * ST doc - RM0008 * Reference manual - STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx * and STM32F107xx advanced ARM-based 32-bit MCUs * ST doc - RM0091 * Reference manual - STM32F0x1/STM32F0x2/STM32F0x8 * advanced ARMĀ®-based 32-bit MCUs * ST doc - RM0360 * Reference manual - STM32F030x4/x6/x8/xC and STM32F070x6/xB * ST doc - PM0075 * Programming manual - STM32F10xxx Flash memory microcontrollers */ #include "general.h" #include "target.h" #include "target_internal.h" #include "cortexm.h" static bool stm32f1_cmd_erase_mass(target *t, int argc, const char **argv); static bool stm32f1_cmd_option(target *t, int argc, const char **argv); const struct command_s stm32f1_cmd_list[] = { {"erase_mass", (cmd_handler)stm32f1_cmd_erase_mass, "Erase entire flash memory"}, {"option", (cmd_handler)stm32f1_cmd_option, "Manipulate option bytes"}, {NULL, NULL, NULL} }; static int stm32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len); static int stm32f1_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); /* Flash Program ad Erase Controller Register Map */ #define FPEC_BASE 0x40022000 #define FLASH_ACR (FPEC_BASE+0x00) #define FLASH_KEYR (FPEC_BASE+0x04) #define FLASH_OPTKEYR (FPEC_BASE+0x08) #define FLASH_SR (FPEC_BASE+0x0C) #define FLASH_CR (FPEC_BASE+0x10) #define FLASH_AR (FPEC_BASE+0x14) #define FLASH_OBR (FPEC_BASE+0x1C) #define FLASH_WRPR (FPEC_BASE+0x20) #define FLASH_BANK2_OFFSET 0x40 #define FLASH_BANK_SPLIT 0x08080000 #define FLASH_CR_OBL_LAUNCH (1<<13) #define FLASH_CR_OPTWRE (1 << 9) #define FLASH_CR_STRT (1 << 6) #define FLASH_CR_OPTER (1 << 5) #define FLASH_CR_OPTPG (1 << 4) #define FLASH_CR_MER (1 << 2) #define FLASH_CR_PER (1 << 1) #define FLASH_CR_PG (1 << 0) #define FLASH_OBR_RDPRT (1 << 1) #define FLASH_SR_BSY (1 << 0) #define FLASH_OBP_RDP 0x1FFFF800 #define FLASH_OBP_RDP_KEY 0x5aa5 #define FLASH_OBP_RDP_KEY_F3 0x55AA #define KEY1 0x45670123 #define KEY2 0xCDEF89AB #define SR_ERROR_MASK 0x14 #define SR_EOP 0x20 #define DBGMCU_IDCODE 0xE0042000 #define DBGMCU_IDCODE_F0 0x40015800 #define FLASHSIZE 0x1FFFF7E0 #define FLASHSIZE_F0 0x1FFFF7CC static void stm32f1_add_flash(target *t, uint32_t addr, size_t length, size_t erasesize) { struct target_flash *f = calloc(1, sizeof(*f)); if (!f) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return; } f->start = addr; f->length = length; f->blocksize = erasesize; f->erase = stm32f1_flash_erase; f->write = stm32f1_flash_write; f->buf_size = erasesize; f->erased = 0xff; target_add_flash(t, f); } /** \brief identify the correct gd32 f1/f3 chip GD32 : STM32 compatible chip */ bool gd32f1_probe(target *t) { uint16_t stored_idcode = t->idcode; // M3 & M4 & riscV only afaik t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff; uint32_t signature= target_mem_read32(t, FLASHSIZE); uint32_t flashSize=signature & 0xFFFF; uint32_t ramSize=signature >>16 ; switch(t->idcode) { case 0x414: /* Gigadevice gd32f303 */ t->driver = "GD32F3"; break; case 0x410: /* Gigadevice gd32f103 */ t->driver = "GD32F1"; break; default: t->idcode = stored_idcode; return false; } target_add_ram(t, 0x20000000, ramSize*1024); stm32f1_add_flash(t, 0x8000000, flashSize*1024, 0x400); target_add_commands(t, stm32f1_cmd_list, t->driver); return true; } /** \brief identify the stm32f1 chip */ bool stm32f1_probe(target *t) { uint16_t stored_idcode = t->idcode; if ((t->cpuid & CPUID_PARTNO_MASK) == CORTEX_M0) t->idcode = target_mem_read32(t, DBGMCU_IDCODE_F0) & 0xfff; else t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff; size_t flash_size; size_t block_size = 0x400; switch(t->idcode) { case 0x29b: /* CS clone */ case 0x410: /* Medium density */ case 0x412: /* Low density */ case 0x420: /* Value Line, Low-/Medium density */ target_add_ram(t, 0x20000000, 0x5000); stm32f1_add_flash(t, 0x8000000, 0x20000, 0x400); target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); /* Test for non-genuine parts with Core rev 2*/ ADIv5_AP_t *ap = cortexm_ap(t); if ((ap->idr >> 28) > 1) { t->driver = "STM32F1 (clone) medium density"; #if defined(PLATFORM_HAS_DEBUG) DEBUG_WARN("Non-genuine STM32F1\n"); #endif } else { t->driver = "STM32F1 medium density"; } return true; case 0x414: /* High density */ case 0x418: /* Connectivity Line */ case 0x428: /* Value Line, High Density */ t->driver = "STM32F1 VL density"; target_add_ram(t, 0x20000000, 0x10000); stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); target_add_commands(t, stm32f1_cmd_list, "STM32 HF/CL/VL-HD"); return true; case 0x430: /* XL-density */ t->driver = "STM32F1 XL density"; target_add_ram(t, 0x20000000, 0x18000); stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); stm32f1_add_flash(t, 0x8080000, 0x80000, 0x800); target_add_commands(t, stm32f1_cmd_list, "STM32 XL/VL-XL"); return true; case 0x438: /* STM32F303x6/8 and STM32F328 */ case 0x422: /* STM32F30x */ case 0x446: /* STM32F303xD/E and STM32F398xE */ target_add_ram(t, 0x10000000, 0x4000); /* fall through */ case 0x432: /* STM32F37x */ case 0x439: /* STM32F302C8 */ t->driver = "STM32F3"; target_add_ram(t, 0x20000000, 0x10000); stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); target_add_commands(t, stm32f1_cmd_list, "STM32F3"); return true; case 0x444: /* STM32F03 RM0091 Rev.7, STM32F030x[4|6] RM0360 Rev. 4*/ t->driver = "STM32F03"; flash_size = 0x8000; break; case 0x445: /* STM32F04 RM0091 Rev.7, STM32F070x6 RM0360 Rev. 4*/ t->driver = "STM32F04/F070x6"; flash_size = 0x8000; break; case 0x440: /* STM32F05 RM0091 Rev.7, STM32F030x8 RM0360 Rev. 4*/ t->driver = "STM32F05/F030x8"; flash_size = 0x10000; break; case 0x448: /* STM32F07 RM0091 Rev.7, STM32F070xB RM0360 Rev. 4*/ t->driver = "STM32F07"; flash_size = 0x20000; block_size = 0x800; break; case 0x442: /* STM32F09 RM0091 Rev.7, STM32F030xC RM0360 Rev. 4*/ t->driver = "STM32F09/F030xC"; flash_size = 0x40000; block_size = 0x800; break; default: /* NONE */ t->idcode = stored_idcode; return false; } target_add_ram(t, 0x20000000, 0x5000); stm32f1_add_flash(t, 0x8000000, flash_size, block_size); target_add_commands(t, stm32f1_cmd_list, "STM32F0"); return true; } static void stm32f1_flash_unlock(target *t, uint32_t bank_offset) { target_mem_write32(t, FLASH_KEYR + bank_offset, KEY1); target_mem_write32(t, FLASH_KEYR + bank_offset, KEY2); } static int stm32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len) { target *t = f->t; target_addr end = addr + len - 1; target_addr start = addr; if ((t->idcode == 0x430) && (end >= FLASH_BANK_SPLIT)) stm32f1_flash_unlock(t, FLASH_BANK2_OFFSET); if (addr < FLASH_BANK_SPLIT) stm32f1_flash_unlock(t, 0); while(len) { uint32_t bank_offset = 0; if (addr >= FLASH_BANK_SPLIT) bank_offset = FLASH_BANK2_OFFSET; /* Flash page erase instruction */ target_mem_write32(t, FLASH_CR + bank_offset, FLASH_CR_PER); /* write address to FMA */ target_mem_write32(t, FLASH_AR + bank_offset, addr); /* Flash page erase start instruction */ target_mem_write32(t, FLASH_CR + bank_offset, FLASH_CR_STRT | FLASH_CR_PER); /* Read FLASH_SR to poll for BSY bit */ while (target_mem_read32(t, FLASH_SR + bank_offset) & FLASH_SR_BSY) if(target_check_error(t)) { DEBUG_WARN("stm32f1 flash erase: comm error\n"); return -1; } if (len > f->blocksize) len -= f->blocksize; else len = 0; addr += f->blocksize; } /* Check for error */ if (start < FLASH_BANK_SPLIT) { uint32_t sr = target_mem_read32(t, FLASH_SR); if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) { DEBUG_INFO("stm32f1 flash erase error 0x%" PRIx32 "\n", sr); return -1; } } if ((t->idcode == 0x430) && (end >= FLASH_BANK_SPLIT)) { uint32_t sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) { DEBUG_INFO("stm32f1 bank 2 flash erase error 0x%" PRIx32 "\n", sr); return -1; } } return 0; } static int stm32f1_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) { target *t = f->t; uint32_t sr; size_t length = 0; if (dest < FLASH_BANK_SPLIT) { if ((dest + len - 1) >= FLASH_BANK_SPLIT) length = FLASH_BANK_SPLIT - dest; else length = len; target_mem_write32(t, FLASH_CR, FLASH_CR_PG); cortexm_mem_write_sized(t, dest, src, length, ALIGN_HALFWORD); /* Read FLASH_SR to poll for BSY bit */ /* Wait for completion or an error */ do { sr = target_mem_read32(t, FLASH_SR); if(target_check_error(t)) { DEBUG_WARN("stm32f1 flash write: comm error\n"); return -1; } } while (sr & FLASH_SR_BSY); if (sr & SR_ERROR_MASK) { DEBUG_WARN("stm32f1 flash write error 0x%" PRIx32 "\n", sr); return -1; } dest += length; src += length; } length = len - length; if ((t->idcode == 0x430) && length) { /* Write on bank 2 */ target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG); cortexm_mem_write_sized(t, dest, src, length, ALIGN_HALFWORD); /* Read FLASH_SR to poll for BSY bit */ /* Wait for completion or an error */ do { sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); if(target_check_error(t)) { DEBUG_WARN("stm32f1 flash bank2 write: comm error\n"); return -1; } } while (sr & FLASH_SR_BSY); if (sr & SR_ERROR_MASK) { DEBUG_WARN("stm32f1 flash bank2 write error 0x%" PRIx32 "\n", sr); return -1; } } return 0; } static bool stm32f1_cmd_erase_mass(target *t, int argc, const char **argv) { (void)argc; (void)argv; stm32f1_flash_unlock(t, 0); /* Flash mass erase start instruction */ target_mem_write32(t, FLASH_CR, FLASH_CR_MER); target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER); /* Read FLASH_SR to poll for BSY bit */ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) if(target_check_error(t)) return false; /* Check for error */ uint16_t sr = target_mem_read32(t, FLASH_SR); if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) return false; if (t->idcode == 0x430) { stm32f1_flash_unlock(t, FLASH_BANK2_OFFSET); /* Flash mass erase start instruction on bank 2*/ target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_MER); target_mem_write32(t, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_STRT | FLASH_CR_MER); /* Read FLASH_SR to poll for BSY bit */ while (target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET) & FLASH_SR_BSY) if(target_check_error(t)) return false; /* Check for error */ uint16_t sr = target_mem_read32(t, FLASH_SR + FLASH_BANK2_OFFSET); if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP)) return false; } return true; } static bool stm32f1_option_erase(target *t) { /* Erase option bytes instruction */ target_mem_write32(t, FLASH_CR, FLASH_CR_OPTER | FLASH_CR_OPTWRE); target_mem_write32(t, FLASH_CR, FLASH_CR_STRT | FLASH_CR_OPTER | FLASH_CR_OPTWRE); /* Read FLASH_SR to poll for BSY bit */ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) if(target_check_error(t)) return false; return true; } static bool stm32f1_option_write_erased(target *t, uint32_t addr, uint16_t value) { if (value == 0xffff) return true; /* Erase option bytes instruction */ target_mem_write32(t, FLASH_CR, FLASH_CR_OPTPG | FLASH_CR_OPTWRE); target_mem_write16(t, addr, value); /* Read FLASH_SR to poll for BSY bit */ while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) if(target_check_error(t)) return false; return true; } static bool stm32f1_option_write(target *t, uint32_t addr, uint16_t value) { uint16_t opt_val[8]; int i, index; index = (addr - FLASH_OBP_RDP) / 2; if ((index < 0) || (index > 7)) return false; /* Retrieve old values */ for (i = 0; i < 16; i = i +4) { uint32_t val = target_mem_read32(t, FLASH_OBP_RDP + i); opt_val[i/2] = val & 0xffff; opt_val[i/2 +1] = val >> 16; } if (opt_val[index] == value) return true; /* Check for erased value */ if (opt_val[index] != 0xffff) if (!(stm32f1_option_erase(t))) return false; opt_val[index] = value; /* Write changed values*/ for (i = 0; i < 8; i++) if (!(stm32f1_option_write_erased (t, FLASH_OBP_RDP + i*2,opt_val[i]))) return false; return true; } static bool stm32f1_cmd_option(target *t, int argc, const char **argv) { uint32_t addr, val; uint32_t flash_obp_rdp_key; uint32_t rdprt; switch(t->idcode) { case 0x422: /* STM32F30x */ case 0x432: /* STM32F37x */ case 0x438: /* STM32F303x6/8 and STM32F328 */ case 0x440: /* STM32F0 */ case 0x446: /* STM32F303xD/E and STM32F398xE */ case 0x445: /* STM32F04 RM0091 Rev.7, STM32F070x6 RM0360 Rev. 4*/ case 0x448: /* STM32F07 RM0091 Rev.7, STM32F070xB RM0360 Rev. 4*/ case 0x442: /* STM32F09 RM0091 Rev.7, STM32F030xC RM0360 Rev. 4*/ flash_obp_rdp_key = FLASH_OBP_RDP_KEY_F3; break; default: flash_obp_rdp_key = FLASH_OBP_RDP_KEY; } rdprt = target_mem_read32(t, FLASH_OBR) & FLASH_OBR_RDPRT; stm32f1_flash_unlock(t, 0); target_mem_write32(t, FLASH_OPTKEYR, KEY1); target_mem_write32(t, FLASH_OPTKEYR, KEY2); if ((argc == 2) && !strcmp(argv[1], "erase")) { stm32f1_option_erase(t); stm32f1_option_write_erased(t, FLASH_OBP_RDP, flash_obp_rdp_key); } else if (rdprt) { tc_printf(t, "Device is Read Protected\n"); tc_printf(t, "Use \"monitor option erase\" to unprotect, erasing device\n"); return true; } else if (argc == 3) { addr = strtol(argv[1], NULL, 0); val = strtol(argv[2], NULL, 0); stm32f1_option_write(t, addr, val); } else { tc_printf(t, "usage: monitor option erase\n"); tc_printf(t, "usage: monitor option \n"); } if (0 && flash_obp_rdp_key == FLASH_OBP_RDP_KEY_F3) { /* Reload option bytes on F0 and F3*/ val = target_mem_read32(t, FLASH_CR); val |= FLASH_CR_OBL_LAUNCH; stm32f1_option_write(t, FLASH_CR, val); val &= ~FLASH_CR_OBL_LAUNCH; stm32f1_option_write(t, FLASH_CR, val); } for (int i = 0; i < 0xf; i += 4) { addr = 0x1ffff800 + i; val = target_mem_read32(t, addr); tc_printf(t, "0x%08X: 0x%04X\n", addr, val & 0xFFFF); tc_printf(t, "0x%08X: 0x%04X\n", addr + 2, val >> 16); } return true; }