diff options
Diffstat (limited to 'src/stlink-lib/common.c')
-rw-r--r-- | src/stlink-lib/common.c | 1372 |
1 files changed, 1372 insertions, 0 deletions
diff --git a/src/stlink-lib/common.c b/src/stlink-lib/common.c new file mode 100644 index 0000000..3e49a77 --- /dev/null +++ b/src/stlink-lib/common.c @@ -0,0 +1,1372 @@ +/* == nightwalker-87: TODO: CONTENT AND USE OF THIS SOURCE FILE IS TO BE VERIFIED (07.06.2023) == */ +/* TODO: This file should be split up into new or existing modules. */ + +/* + * File: common.c + * + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +// #include <sys/stat.h> // TODO: Check use +// #include <sys/types.h> // TODO: Check use + +#include <stlink.h> + +#include "calculate.h" +#include "chipid.h" +#include "common_flash.h" +#include "helper.h" +#include "logging.h" +#include "map_file.h" +#include "md5.h" +#include "read_write.h" +#include "register.h" +#include "usb.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef _MSC_VER +#define __attribute__(x) +#endif + +// Private structs and functions defines +struct stlink_fread_worker_arg { + int32_t fd; +}; + +struct stlink_fread_ihex_worker_arg { + FILE *file; + uint32_t addr; + uint32_t lba; + uint8_t buf[16]; + uint8_t buf_pos; +}; + +typedef bool (*save_block_fn)(void *arg, uint8_t *block, ssize_t len); + +static void stop_wdg_in_debug(stlink_t *); +int32_t stlink_jtag_reset(stlink_t *, int32_t); +int32_t stlink_soft_reset(stlink_t *, int32_t); +void _parse_version(stlink_t *, stlink_version_t *); +static uint8_t stlink_parse_hex(const char *); +static int32_t stlink_read(stlink_t *, stm32_addr_t, uint32_t, save_block_fn, void *); +static bool stlink_fread_ihex_init(struct stlink_fread_ihex_worker_arg *, int32_t, stm32_addr_t); +static bool stlink_fread_ihex_worker(void *, uint8_t *, ssize_t); +static bool stlink_fread_ihex_finalize(struct stlink_fread_ihex_worker_arg *); +static bool stlink_fread_worker(void *, uint8_t *, ssize_t); +// End of private structs and functions defines + +// Functions below are defined in stlink.h (see line num before function) +// 252 +void stlink_close(stlink_t *sl) { + DLOG("*** stlink_close ***\n"); + + if (!sl) { + return; + } + + sl->backend->close(sl); + free(sl); +} + +// 250 +int32_t stlink_exit_debug_mode(stlink_t *sl) { + DLOG("*** stlink_exit_debug_mode ***\n"); + + if (sl->flash_type != STM32_FLASH_TYPE_UNKNOWN && + sl->core_stat != TARGET_RESET) { + // stop debugging if the target has been identified + stlink_write_debug32(sl, STLINK_REG_DHCSR, STLINK_REG_DHCSR_DBGKEY); + } + + return (sl->backend->exit_debug_mode(sl)); +} + +//248 +int32_t stlink_enter_swd_mode(stlink_t *sl) { + DLOG("*** stlink_enter_swd_mode ***\n"); + return (sl->backend->enter_swd_mode(sl)); +} + +// 271 +// Force the core into the debug mode -> halted state. +int32_t stlink_force_debug(stlink_t *sl) { + DLOG("*** stlink_force_debug_mode ***\n"); + int32_t res = sl->backend->force_debug(sl); + if (res) { + return (res); + } + // Stop the watchdogs in the halted state for suppress target reboot + stop_wdg_in_debug(sl); + return (0); +} + +// 251 +int32_t stlink_exit_dfu_mode(stlink_t *sl) { + DLOG("*** stlink_exit_dfu_mode ***\n"); + return (sl->backend->exit_dfu_mode(sl)); +} + +// 253 +int32_t stlink_core_id(stlink_t *sl) { + int32_t ret; + + DLOG("*** stlink_core_id ***\n"); + ret = sl->backend->core_id(sl); + + if (ret == -1) { + ELOG("Failed to read core_id\n"); + return (ret); + } + + if (sl->verbose > 2) { + stlink_print_data(sl); + } + + DLOG("core_id = 0x%08x\n", sl->core_id); + return (ret); +} + +// 287 +// stlink_chip_id() is called by stlink_load_device_params() +// do not call this procedure directly. +int32_t stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { + int32_t ret; + cortex_m3_cpuid_t cpu_id; + + // Read the CPU ID to determine where to read the core id + if (stlink_cpu_id(sl, &cpu_id) || + cpu_id.implementer_id != STLINK_REG_CMx_CPUID_IMPL_ARM) { + ELOG("Can not connect to target. Please use \'connect under reset\' and try again\n"); + return -1; + } + + /* + * the chip_id register in the reference manual have + * DBGMCU_IDCODE / DBG_IDCODE name + */ + + if ((sl->core_id == STM32_CORE_ID_M7F_M33_SWD || sl->core_id == STM32_CORE_ID_M7F_M33_JTAG) && + cpu_id.part == STLINK_REG_CMx_CPUID_PARTNO_CM7) { + // STM32H7 chipid in 0x5c001000 (RM0433 pg3189) + ret = stlink_read_debug32(sl, 0x5c001000, chip_id); + } else if (cpu_id.part == STLINK_REG_CMx_CPUID_PARTNO_CM0 || + cpu_id.part == STLINK_REG_CMx_CPUID_PARTNO_CM0P) { + // STM32F0 (RM0091, pg914; RM0360, pg713) + // STM32L0 (RM0377, pg813; RM0367, pg915; RM0376, pg917) + // STM32G0 (RM0444, pg1367) + ret = stlink_read_debug32(sl, 0x40015800, chip_id); + } else if (cpu_id.part == STLINK_REG_CMx_CPUID_PARTNO_CM33) { + // STM32L5 (RM0438, pg2157) + ret = stlink_read_debug32(sl, 0xE0044000, chip_id); + } else /* СM3, СM4, CM7 */ { + // default chipid address + + // STM32F1 (RM0008, pg1087; RM0041, pg681) + // STM32F2 (RM0033, pg1326) + // STM32F3 (RM0316, pg1095; RM0313, pg874) + // STM32F7 (RM0385, pg1676; RM0410, pg1912) + // STM32L1 (RM0038, pg861) + // STM32L4 (RM0351, pg1840; RM0394, pg1560) + // STM32G4 (RM0440, pg2086) + // STM32WB (RM0434, pg1406) + ret = stlink_read_debug32(sl, 0xE0042000, chip_id); + } + + if (ret || !(*chip_id)) { + *chip_id = 0; + ret = ret?ret:-1; + ELOG("Could not find chip id!\n"); + } else { + *chip_id = (*chip_id) & 0xfff; + + // Fix chip_id for F4 rev A errata, read CPU ID, as CoreID is the same for + // F2/F4 + if (*chip_id == 0x411 && cpu_id.part == STLINK_REG_CMx_CPUID_PARTNO_CM4) { + *chip_id = 0x413; + } + } + + return (ret); +} + +// 288 +/** + * Cortex M tech ref manual, CPUID register description + * @param sl stlink context + * @param cpuid pointer to the result object + */ +int32_t stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid) { + uint32_t raw; + + if (stlink_read_debug32(sl, STLINK_REG_CM3_CPUID, &raw)) { + cpuid->implementer_id = 0; + cpuid->variant = 0; + cpuid->part = 0; + cpuid->revision = 0; + return (-1); + } + + cpuid->implementer_id = (raw >> 24) & 0x7f; + cpuid->variant = (raw >> 20) & 0xf; + cpuid->part = (raw >> 4) & 0xfff; + cpuid->revision = raw & 0xf; + return (0); +} + +// 303 +/** + * Reads and decodes the flash parameters, as dynamically as possible + * @param sl + * @return 0 for success, or -1 for unsupported core type. + */ +int32_t stlink_load_device_params(stlink_t *sl) { + // This seems to normally work so is unnecessary info for a normal user. + // Demoted to debug. -- REW + DLOG("Loading device parameters....\n"); + const struct stlink_chipid_params *params = NULL; + stlink_core_id(sl); + uint32_t flash_size; + + if (stlink_chip_id(sl, &sl->chip_id)) { + return (-1); + } + + params = stlink_chipid_get_params(sl->chip_id); + + if (params == NULL) { + WLOG("unknown chip id! %#x\n", sl->chip_id); + return (-1); + } + + if (params->flash_type == STM32_FLASH_TYPE_UNKNOWN) { + WLOG("Invalid flash type, please check device declaration\n"); + sl->flash_size = 0; + return (0); + } + + // These are fixed... + sl->flash_base = STM32_FLASH_BASE; + sl->sram_base = STM32_SRAM_BASE; + stlink_read_debug32(sl, (params->flash_size_reg) & ~3, &flash_size); + + if (params->flash_size_reg & 2) { + flash_size = flash_size >> 16; + } + + flash_size = flash_size & 0xffff; + + if ((sl->chip_id == STM32_CHIPID_L1_MD || + sl->chip_id == STM32_CHIPID_F1_VL_MD_LD || + sl->chip_id == STM32_CHIPID_L1_MD_PLUS) && + (flash_size == 0)) { + sl->flash_size = 128 * 1024; + } else if (sl->chip_id == STM32_CHIPID_L1_CAT2) { + sl->flash_size = (flash_size & 0xff) * 1024; + } else if ((sl->chip_id & 0xFFF) == STM32_CHIPID_L1_MD_PLUS_HD) { + // 0 is 384k and 1 is 256k + if (flash_size == 0) { + sl->flash_size = 384 * 1024; + } else { + sl->flash_size = 256 * 1024; + } + } else { + sl->flash_size = flash_size * 1024; + } + + sl->flash_type = params->flash_type; + sl->flash_pgsz = params->flash_pagesize; + sl->sram_size = params->sram_size; + sl->sys_base = params->bootrom_base; + sl->sys_size = params->bootrom_size; + sl->option_base = params->option_base; + sl->option_size = params->option_size; + sl->chip_flags = params->flags; + sl->otp_base = params->otp_base; + sl->otp_size = params->otp_size; + + // medium and low devices have the same chipid. ram size depends on flash + // size. STM32F100xx datasheet Doc ID 16455 Table 2 + if (sl->chip_id == STM32_CHIPID_F1_VL_MD_LD && sl->flash_size < 64 * 1024) { + sl->sram_size = 0x1000; + } + + if (sl->chip_id == STM32_CHIPID_G4_CAT3 || + sl->chip_id == STM32_CHIPID_G4_CAT4) { + uint32_t flash_optr; + stlink_read_debug32(sl, FLASH_Gx_OPTR, &flash_optr); + + if (!(flash_optr & (1 << FLASH_G4_OPTR_DBANK))) { + sl->flash_pgsz <<= 1; + } + } + + if (sl->chip_id == STM32_CHIPID_L5x2xx) { + uint32_t flash_optr; + stlink_read_debug32(sl, FLASH_L5_OPTR, &flash_optr); + + if (sl->flash_size == 512*1024 && (flash_optr & (1 << 22)) != 0) { + sl->flash_pgsz = 0x800; + } + } + + // H7 devices with small flash has one bank + if (sl->chip_flags & CHIP_F_HAS_DUAL_BANK && + sl->flash_type == STM32_FLASH_TYPE_H7) { + if ((sl->flash_size / sl->flash_pgsz) <= 1) + sl->chip_flags &= ~CHIP_F_HAS_DUAL_BANK; + } + + ILOG("%s: %u KiB SRAM, %u KiB flash in at least %u %s pages.\n", + params->dev_type, (sl->sram_size / 1024), (sl->flash_size / 1024), + (sl->flash_pgsz < 1024) ? sl->flash_pgsz : (sl->flash_pgsz / 1024), + (sl->flash_pgsz < 1024) ? "byte" : "KiB"); + + return (0); +} + +// 254 +int32_t stlink_reset(stlink_t *sl, enum reset_type type) { + uint32_t dhcsr; + uint32_t timeout; + + DLOG("*** stlink_reset ***\n"); + + sl->core_stat = TARGET_RESET; + + if (type == RESET_AUTO) { + // clear S_RESET_ST in DHCSR register for reset state detection + stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + } + + if (type == RESET_HARD || type == RESET_AUTO) { + // hardware target reset + if (sl->version.stlink_v > 1) { + stlink_jtag_reset(sl, STLINK_DEBUG_APIV2_DRIVE_NRST_LOW); + // minimum reset pulse duration of 20 us (RM0008, 8.1.2 Power reset) + usleep(100); + stlink_jtag_reset(sl, STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH); + } + sl->backend->reset(sl); + usleep(10000); + } + + if (type == RESET_AUTO) { + /* Check if the S_RESET_ST bit is set in DHCSR + * This means that a reset has occurred + * DDI0337E, p. 10-4, Debug Halting Control and Status Register + */ + + dhcsr = 0; + int32_t res = stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + if ((dhcsr & STLINK_REG_DHCSR_S_RESET_ST) == 0 && !res) { + // reset not done yet --> try reset through AIRCR so that NRST does not need to be connected + ILOG("NRST is not connected --> using software reset via AIRCR\n"); + DLOG("NRST not connected --> Reset through SYSRESETREQ\n"); + return stlink_soft_reset(sl, 0); + } + + // waiting for reset the S_RESET_ST bit within 500ms + timeout = time_ms() + 500; + while (time_ms() < timeout) { + dhcsr = STLINK_REG_DHCSR_S_RESET_ST; + stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + if ((dhcsr & STLINK_REG_DHCSR_S_RESET_ST) == 0) { + return (0); + } + } + + return (-1); + } + + if (type == RESET_SOFT || type == RESET_SOFT_AND_HALT) { + return stlink_soft_reset(sl, (type == RESET_SOFT_AND_HALT)); + } + + return (0); +} + +int32_t stlink_soft_reset(stlink_t *sl, int32_t halt_on_reset) { + int32_t ret; + uint32_t timeout; + uint32_t dhcsr, dfsr; + + DLOG("*** stlink_soft_reset %s***\n", halt_on_reset ? "(halt) " : ""); + + // halt core and enable debugging (if not already done) + // C_DEBUGEN is required to Halt on reset (DDI0337E, p. 10-6) + stlink_write_debug32(sl, STLINK_REG_DHCSR, + STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_HALT | + STLINK_REG_DHCSR_C_DEBUGEN); + + // enable Halt on reset by set VC_CORERESET and TRCENA (DDI0337E, p. 10-10) + if (halt_on_reset) { + stlink_write_debug32( + sl, STLINK_REG_CM3_DEMCR, + STLINK_REG_CM3_DEMCR_TRCENA | STLINK_REG_CM3_DEMCR_VC_HARDERR | + STLINK_REG_CM3_DEMCR_VC_BUSERR | STLINK_REG_CM3_DEMCR_VC_CORERESET); + + // clear VCATCH in the DFSR register + stlink_write_debug32(sl, STLINK_REG_DFSR, STLINK_REG_DFSR_VCATCH); + } else { + stlink_write_debug32(sl, STLINK_REG_CM3_DEMCR, + STLINK_REG_CM3_DEMCR_TRCENA | + STLINK_REG_CM3_DEMCR_VC_HARDERR | + STLINK_REG_CM3_DEMCR_VC_BUSERR); + } + + // clear S_RESET_ST in the DHCSR register + stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + + // soft reset (core reset) by SYSRESETREQ (DDI0337E, p. 8-23) + ret = stlink_write_debug32(sl, STLINK_REG_AIRCR, + STLINK_REG_AIRCR_VECTKEY | + STLINK_REG_AIRCR_SYSRESETREQ); + if (ret) { + ELOG("Soft reset failed: error write to AIRCR\n"); + return (ret); + } + + // waiting for a reset within 500ms + // DDI0337E, p. 10-4, Debug Halting Control and Status Register + timeout = time_ms() + 500; + while (time_ms() < timeout) { + // DDI0337E, p. 10-4, Debug Halting Control and Status Register + dhcsr = STLINK_REG_DHCSR_S_RESET_ST; + stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + if ((dhcsr & STLINK_REG_DHCSR_S_RESET_ST) == 0) { + if (halt_on_reset) { + // waiting halt by the SYSRESETREQ exception + // DDI0403E, p. C1-699, Debug Fault Status Register + dfsr = 0; + stlink_read_debug32(sl, STLINK_REG_DFSR, &dfsr); + if ((dfsr & STLINK_REG_DFSR_VCATCH) == 0) { + continue; + } + } + timeout = 0; + break; + } + } + + // reset DFSR register. DFSR is power-on reset only (DDI0337H, p. 7-5) + stlink_write_debug32(sl, STLINK_REG_DFSR, STLINK_REG_DFSR_CLEAR); + + if (timeout) { + ELOG("Soft reset failed: timeout\n"); + return (-1); + } + + return (0); +} + +// 255 +int32_t stlink_run(stlink_t *sl, enum run_type type) { + struct stlink_reg rr; + DLOG("*** stlink_run ***\n"); + + /* Make sure we are in Thumb mode + * Cortex-M chips don't support ARM mode instructions + * xPSR may be incorrect if the vector table has invalid data */ + stlink_read_reg(sl, 16, &rr); + if ((rr.xpsr & (1 << 24)) == 0) { + ILOG("Go to Thumb mode\n"); + stlink_write_reg(sl, rr.xpsr | (1 << 24), 16); + } + + return (sl->backend->run(sl, type)); +} + +// 273 +int32_t stlink_set_swdclk(stlink_t *sl, int32_t freq_khz) { + DLOG("*** set_swdclk ***\n"); + return (sl->backend->set_swdclk(sl, freq_khz)); +} + +// 293 +// this function is called by stlink_status() +// do not call stlink_core_stat() directly, always use stlink_status() +void stlink_core_stat(stlink_t *sl) { + switch (sl->core_stat) { + case TARGET_RUNNING: + DLOG(" core status: running\n"); + return; + case TARGET_HALTED: + DLOG(" core status: halted\n"); + return; + case TARGET_RESET: + DLOG(" core status: reset\n"); + return; + case TARGET_DEBUG_RUNNING: + DLOG(" core status: debug running\n"); + return; + default: + DLOG(" core status: unknown\n"); + } +} + +// 256 +int32_t stlink_status(stlink_t *sl) { + int32_t ret; + + DLOG("*** stlink_status ***\n"); + ret = sl->backend->status(sl); + stlink_core_stat(sl); + return (ret); +} + +// 257 +int32_t stlink_version(stlink_t *sl) { + DLOG("*** looking up stlink version ***\n"); + + if (sl->backend->version(sl)) { + return (-1); + } + + _parse_version(sl, &sl->version); + + DLOG("st vid = 0x%04x (expect 0x%04x)\n", sl->version.st_vid, + STLINK_USB_VID_ST); + DLOG("stlink pid = 0x%04x\n", sl->version.stlink_pid); + DLOG("stlink version = 0x%x\n", sl->version.stlink_v); + DLOG("jtag version = 0x%x\n", sl->version.jtag_v); + DLOG("swim version = 0x%x\n", sl->version.swim_v); + + if (sl->version.jtag_v == 0) { + WLOG(" warning: stlink doesn't support JTAG/SWD interface\n"); + } + + return (0); +} + +// 272 +int32_t stlink_target_voltage(stlink_t *sl) { + int32_t voltage = -1; + DLOG("*** reading target voltage\n"); + + if (sl->backend->target_voltage != NULL) { + voltage = sl->backend->target_voltage(sl); + + if (voltage != -1) { + DLOG("target voltage = %imV\n", voltage); + } else { + DLOG("error reading target voltage\n"); + } + } else { + DLOG("reading voltage not supported by backend\n"); + } + + return (voltage); +} + +// 299 +bool stlink_is_core_halted(stlink_t *sl) { + stlink_status(sl); + return (sl->core_stat == TARGET_HALTED); +} + +// 269 +int32_t stlink_step(stlink_t *sl) { + DLOG("*** stlink_step ***\n"); + return (sl->backend->step(sl)); +} + +// 270 +int32_t stlink_current_mode(stlink_t *sl) { + int32_t mode = sl->backend->current_mode(sl); + + switch (mode) { + case STLINK_DEV_DFU_MODE: + DLOG("stlink current mode: dfu\n"); + return (mode); + case STLINK_DEV_DEBUG_MODE: + DLOG("stlink current mode: debug (jtag or swd)\n"); + return (mode); + case STLINK_DEV_MASS_MODE: + DLOG("stlink current mode: mass\n"); + return (mode); + } + + DLOG("stlink mode: unknown!\n"); + return (STLINK_DEV_UNKNOWN_MODE); +} + +// 294 +void stlink_print_data(stlink_t *sl) { + if (sl->q_len <= 0 || sl->verbose < UDEBUG) { + return; + } + + if (sl->verbose > 2) { + DLOG("data_len = %d 0x%x\n", sl->q_len, sl->q_len); + } + + for (int32_t i = 0; i < sl->q_len; i++) { + if (i % 16 == 0) { + /* + if (sl->q_data_dir == Q_DATA_OUT) { + fprintf(stdout, "\n<- 0x%08x ", sl->q_addr + i); + } else { + fprintf(stdout, "\n-> 0x%08x ", sl->q_addr + i); + } + */ + } + // DLOG(" %02x", (uint32_t) sl->q_buf[i]); + fprintf(stderr, " %02x", (uint32_t)sl->q_buf[i]); + } + // DLOG("\n\n"); + fprintf(stderr, "\n"); +} + +// 283 +int32_t stlink_mwrite_sram(stlink_t *sl, uint8_t *data, uint32_t length, stm32_addr_t addr) { + // write the file in sram at addr + + int32_t error = -1; + uint32_t off; + uint32_t len; + + // check addr range is inside the sram + if (addr < sl->sram_base) { + fprintf(stderr, "addr too low\n"); + goto on_error; + } else if ((addr + length) < addr) { + fprintf(stderr, "addr overruns\n"); + goto on_error; + } else if ((addr + length) > (sl->sram_base + sl->sram_size)) { + fprintf(stderr, "addr too high\n"); + goto on_error; + } else if (addr & 3) { + fprintf(stderr, "unaligned addr\n"); + goto on_error; + } + + len = length; + + if (len & 3) { + len -= len & 3; + } + + // do the copy by 1kB blocks + for (off = 0; off < len; off += 1024) { + uint32_t size = 1024; + + if ((off + size) > len) { + size = len - off; + } + + memcpy(sl->q_buf, data + off, size); + + if (size & 3) { + size += 2; + } // round size if needed + + stlink_write_mem32(sl, addr + off, (uint16_t)size); + } + + if (length > len) { + memcpy(sl->q_buf, data + len, length - len); + stlink_write_mem8(sl, addr + len, (uint16_t)(length - len)); + } + + error = 0; // success + stlink_fwrite_finalize(sl, addr); + +on_error: + return (error); +} + +//284 +int32_t stlink_fwrite_sram(stlink_t *sl, const char *path, stm32_addr_t addr) { + // write the file in sram at addr + + int32_t error = -1; + uint32_t off; + uint32_t len; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) { + fprintf(stderr, "map_file() == -1\n"); + return (-1); + } + + printf("file %s ", path); + md5_calculate(&mf); + stlink_checksum(&mf); + + // check if addr range is inside the SRAM + if (addr < sl->sram_base) { + fprintf(stderr, "addr too low\n"); + goto on_error; + } else if ((addr + mf.len) < addr) { + fprintf(stderr, "addr overruns\n"); + goto on_error; + } else if ((addr + mf.len) > (sl->sram_base + sl->sram_size)) { + fprintf(stderr, "addr too high\n"); + goto on_error; + } else if (addr & 3) { + fprintf(stderr, "unaligned addr\n"); + goto on_error; + } + + len = mf.len; + + if (len & 3) { + len -= len & 3; + } + + // do the copy by 1kB blocks + for (off = 0; off < len; off += 1024) { + uint32_t size = 1024; + + if ((off + size) > len) { + size = len - off; + } + + memcpy(sl->q_buf, mf.base + off, size); + + if (size & 3) { + size += 2; + } // round size if needed + + stlink_write_mem32(sl, addr + off, (uint16_t)size); + } + + if (mf.len > len) { + memcpy(sl->q_buf, mf.base + len, mf.len - len); + stlink_write_mem8(sl, addr + len, (uint16_t)(mf.len - len)); + } + + // check the file has been written + if (check_file(sl, &mf, addr) == -1) { + fprintf(stderr, "check_file() == -1\n"); + goto on_error; + } + + error = 0; // success + stlink_fwrite_finalize(sl, addr); + +on_error: + unmap_file(&mf); + return (error); +} + +// 302 +int32_t stlink_fread(stlink_t *sl, const char *path, bool is_ihex, stm32_addr_t addr, uint32_t size) { + // read size bytes from addr to file + ILOG("read from address %#010x size %u\n", addr, size); + + int32_t error; + int32_t fd = open(path, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 00700); + + if (fd == -1) { + fprintf(stderr, "open(%s) == -1\n", path); + return (-1); + } + + if (is_ihex) { + struct stlink_fread_ihex_worker_arg arg; + + if (stlink_fread_ihex_init(&arg, fd, addr)) { + error = stlink_read(sl, addr, size, &stlink_fread_ihex_worker, &arg); + + if (!stlink_fread_ihex_finalize(&arg)) { + error = -1; + } + } else { + error = -1; + } + } else { + struct stlink_fread_worker_arg arg = {fd}; + error = stlink_read(sl, addr, size, &stlink_fread_worker, &arg); + } + + close(fd); + return (error); +} + +// 300 +int32_t write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, uint16_t size) { + // write the buffer right after the loader + int32_t ret = 0; + uint16_t chunk = size & ~0x3; + uint16_t rem = size & 0x3; + + if (chunk) { + memcpy(sl->q_buf, buf, chunk); + ret = stlink_write_mem32(sl, fl->buf_addr, chunk); + } + + if (rem && !ret) { + memcpy(sl->q_buf, buf + chunk, rem); + ret = stlink_write_mem8(sl, (fl->buf_addr) + chunk, rem); + } + + return (ret); +} + +// 291 +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr) { + + if ((sl->chip_id == STM32_CHIPID_F2) || + (sl->chip_id == STM32_CHIPID_F4) || + (sl->chip_id == STM32_CHIPID_F4_DE) || + (sl->chip_id == STM32_CHIPID_F4_LP) || + (sl->chip_id == STM32_CHIPID_F4_HD) || + (sl->chip_id == STM32_CHIPID_F411xx) || + (sl->chip_id == STM32_CHIPID_F446) || + (sl->chip_id == STM32_CHIPID_F4_DSI) || + (sl->chip_id == STM32_CHIPID_F72xxx) || + (sl->chip_id == STM32_CHIPID_F412)) { + uint32_t sector = calculate_F4_sectornum(flashaddr); + + if (sector >= 12) { + sector -= 12; + } + + if (sector < 4) { + sl->flash_pgsz = 0x4000; + } else if (sector < 5) { + sl->flash_pgsz = 0x10000; + } else { + sl->flash_pgsz = 0x20000; + } + } else if (sl->chip_id == STM32_CHIPID_F7 || + sl->chip_id == STM32_CHIPID_F76xxx) { + uint32_t sector = calculate_F7_sectornum(flashaddr); + + if (sector < 4) { + sl->flash_pgsz = 0x8000; + } else if (sector < 5) { + sl->flash_pgsz = 0x20000; + } else { + sl->flash_pgsz = 0x40000; + } + } + + return (sl->flash_pgsz); +} + +// 279 +int32_t stlink_parse_ihex(const char *path, uint8_t erased_pattern, uint8_t **mem, + uint32_t *size, uint32_t *begin) { + int32_t res = 0; + *begin = UINT32_MAX; + uint8_t *data = NULL; + uint32_t end = 0; + bool eof_found = false; + + for (int32_t scan = 0; (res == 0) && (scan < 2); ++scan) { + // parse file two times - first to find memory range, second - to fill it + if (scan == 1) { + if (!eof_found) { + ELOG("No EoF recond\n"); + res = -1; + break; + } + + if (*begin >= end) { + ELOG("No data found in file\n"); + res = -1; + break; + } + + *size = (end - *begin) + 1; + data = calloc(*size, 1); // use calloc to get NULL if out of memory + + if (!data) { + ELOG("Cannot allocate %u bytes\n", (*size)); + res = -1; + break; + } + + memset(data, erased_pattern, *size); + } + + FILE *file = fopen(path, "r"); + + if (!file) { + ELOG("Cannot open file\n"); + res = -1; + break; + } + + uint32_t lba = 0; + char line[1 + 5 * 2 + 255 * 2 + 2]; + + while (fgets(line, sizeof(line), file)) { + if (line[0] == '\n' || line[0] == '\r') { + continue; + } // skip empty lines + + if (line[0] != ':') { // no marker - wrong file format + ELOG("Wrong file format - no marker\n"); + res = -1; + break; + } + + uint32_t l = strlen(line); + + while (l > 0 && (line[l - 1] == '\n' || line[l - 1] == '\r')) { + --l; + } // trim EoL + + if ((l < 11) || + (l == + (sizeof(line) - 1))) { // line too short or long - wrong file format + ELOG("Wrong file format - wrong line length\n"); + res = -1; + break; + } + + uint8_t chksum = 0; // check sum + + for (uint32_t i = 1; i < l; i += 2) { + chksum += stlink_parse_hex(line + i); + } + + if (chksum != 0) { + ELOG("Wrong file format - checksum mismatch\n"); + res = -1; + break; + } + + uint8_t reclen = stlink_parse_hex(line + 1); + + if (((uint32_t)reclen + 5) * 2 + 1 != l) { + ELOG("Wrong file format - record length mismatch\n"); + res = -1; + break; + } + + uint16_t offset = ((uint16_t)stlink_parse_hex(line + 3) << 8) | + ((uint16_t)stlink_parse_hex(line + 5)); + uint8_t rectype = stlink_parse_hex(line + 7); + + switch (rectype) { + case 0: /* Data */ + if (scan == 0) { + uint32_t b = lba + offset; + uint32_t e = b + reclen - 1; + + if (b < *begin) { + *begin = b; + } + + if (e > end) { + end = e; + } + } else { + for (uint8_t i = 0; i < reclen; ++i) { + uint8_t b = stlink_parse_hex(line + 9 + i * 2); + uint32_t addr = lba + offset + i; + + if (addr >= *begin && addr <= end) { + data[addr - *begin] = b; + } + } + } + break; + case 1: /* EoF */ + eof_found = true; + break; + case 2: /* Extended Segment Address, unexpected */ + res = -1; + break; + case 3: /* Start Segment Address, unexpected */ + res = -1; + break; + case 4: /* Extended Linear Address */ + if (reclen == 2) { + lba = ((uint32_t)stlink_parse_hex(line + 9) << 24) | + ((uint32_t)stlink_parse_hex(line + 11) << 16); + } else { + ELOG("Wrong file format - wrong LBA length\n"); + res = -1; + } + break; + case 5: /* Start Linear Address - expected, but ignore */ + break; + default: + ELOG("Wrong file format - unexpected record type %d\n", rectype); + res = -1; + } + + if (res != 0) { + break; + } + } + + fclose(file); + } + + if (res == 0) { + *mem = data; + } else { + free(data); + } + + return (res); +} + +// 280 +uint8_t stlink_get_erased_pattern(stlink_t *sl) { + if (sl->flash_type == STM32_FLASH_TYPE_L0_L1) { + return (0x00); + } else { + return (0xff); + } +} + +// 322 +int32_t stlink_target_connect(stlink_t *sl, enum connect_type connect) { + if (connect == CONNECT_UNDER_RESET) { + stlink_enter_swd_mode(sl); + + stlink_jtag_reset(sl, STLINK_DEBUG_APIV2_DRIVE_NRST_LOW); + + // try to halt the core before reset + // this is useful if the NRST pin is not connected + sl->backend->force_debug(sl); + + // minimum reset pulse duration of 20 us (RM0008, 8.1.2 Power reset) + usleep(20); + + stlink_jtag_reset(sl, STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH); + + // try to halt the core after reset + uint32_t timeout = time_ms() + 10; + while (time_ms() < timeout) { + sl->backend->force_debug(sl); + usleep(100); + } + + // check NRST connection + uint32_t dhcsr = 0; + stlink_read_debug32(sl, STLINK_REG_DHCSR, &dhcsr); + if ((dhcsr & STLINK_REG_DHCSR_S_RESET_ST) == 0) { + WLOG("NRST is not connected\n"); + } + + // addition soft reset for halt before the first instruction + stlink_soft_reset(sl, 1 /* halt on reset */); + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE && + stlink_enter_swd_mode(sl)) { + printf("Failed to enter SWD mode\n"); + return -1; + } + + if (connect == CONNECT_NORMAL) { + stlink_reset(sl, RESET_AUTO); + } + + return stlink_load_device_params(sl); +} + +// End of delegates.... functions below are private to this module +// same as above with entrypoint. + +static void stop_wdg_in_debug(stlink_t *sl) { + uint32_t dbgmcu_cr; + uint32_t set; + uint32_t value; + + switch (sl->flash_type) { + case STM32_FLASH_TYPE_F0_F1_F3: + case STM32_FLASH_TYPE_F1_XL: + case STM32_FLASH_TYPE_G4: + dbgmcu_cr = STM32F0_DBGMCU_CR; + set = (1 << STM32F0_DBGMCU_CR_IWDG_STOP) | + (1 << STM32F0_DBGMCU_CR_WWDG_STOP); + break; + case STM32_FLASH_TYPE_F2_F4: + case STM32_FLASH_TYPE_F7: + case STM32_FLASH_TYPE_L4: + dbgmcu_cr = STM32F4_DBGMCU_APB1FZR1; + set = (1 << STM32F4_DBGMCU_APB1FZR1_IWDG_STOP) | + (1 << STM32F4_DBGMCU_APB1FZR1_WWDG_STOP); + break; + case STM32_FLASH_TYPE_L0_L1: + case STM32_FLASH_TYPE_G0: + if (get_stm32l0_flash_base(sl) == FLASH_Lx_REGS_ADDR) { + dbgmcu_cr = STM32L1_DBGMCU_APB1_FZ; + set = (1 << STM32L1_DBGMCU_APB1_FZ_IWDG_STOP) | + (1 << STM32L1_DBGMCU_APB1_FZ_WWDG_STOP); + } else { + dbgmcu_cr = STM32L0_DBGMCU_APB1_FZ; + set = (1 << STM32L0_DBGMCU_APB1_FZ_IWDG_STOP) | + (1 << STM32L0_DBGMCU_APB1_FZ_WWDG_STOP); + } + break; + case STM32_FLASH_TYPE_H7: + dbgmcu_cr = STM32H7_DBGMCU_APB1HFZ; + set = (1 << STM32H7_DBGMCU_APB1HFZ_IWDG_STOP); + break; + case STM32_FLASH_TYPE_WB_WL: + dbgmcu_cr = STM32WB_DBGMCU_APB1FZR1; + set = (1 << STM32WB_DBGMCU_APB1FZR1_IWDG_STOP) | + (1 << STM32WB_DBGMCU_APB1FZR1_WWDG_STOP); + break; + default: + return; + } + + if (!stlink_read_debug32(sl, dbgmcu_cr, &value)) { + stlink_write_debug32(sl, dbgmcu_cr, value | set); + } +} + +int32_t stlink_jtag_reset(stlink_t *sl, int32_t value) { + DLOG("*** stlink_jtag_reset %d ***\n", value); + return (sl->backend->jtag_reset(sl, value)); +} + +/** + * Decode the version bits, originally from -sg, verified with usb + * @param sl stlink context, assumed to contain valid data in the buffer + * @param slv output parsed version object + */ +void _parse_version(stlink_t *sl, stlink_version_t *slv) { + sl->version.flags = 0; + + if (sl->version.stlink_v < 3) { + uint32_t b0 = sl->q_buf[0]; // lsb + uint32_t b1 = sl->q_buf[1]; + uint32_t b2 = sl->q_buf[2]; + uint32_t b3 = sl->q_buf[3]; + uint32_t b4 = sl->q_buf[4]; + uint32_t b5 = sl->q_buf[5]; // msb + + // b0 b1 || b2 b3 | b4 b5 + // 4b | 6b | 6b || 2B | 2B + // stlink_v | jtag_v | swim_v || st_vid | stlink_pid + + slv->stlink_v = (b0 & 0xf0) >> 4; + slv->jtag_v = ((b0 & 0x0f) << 2) | ((b1 & 0xc0) >> 6); + slv->swim_v = b1 & 0x3f; + slv->st_vid = (b3 << 8) | b2; + slv->stlink_pid = (b5 << 8) | b4; + + // ST-LINK/V1 from J11 switch to api-v2 (and support SWD) + if (slv->stlink_v == 1) { + slv->jtag_api = + slv->jtag_v > 11 ? STLINK_JTAG_API_V2 : STLINK_JTAG_API_V1; + } else { + slv->jtag_api = STLINK_JTAG_API_V2; + + // preferred API to get last R/W status from J15 + if (sl->version.jtag_v >= 15) { + sl->version.flags |= STLINK_F_HAS_GETLASTRWSTATUS2; + } + + if (sl->version.jtag_v >= 13) { + sl->version.flags |= STLINK_F_HAS_TRACE; + sl->max_trace_freq = STLINK_V2_MAX_TRACE_FREQUENCY; + } + } + } else { + // V3 uses different version format, for reference see OpenOCD source + // (that was written from docs available from ST under NDA): + // https://github.com/ntfreak/openocd/blob/a6dacdff58ef36fcdac00c53ec27f19de1fbce0d/src/jtag/drivers/stlink_usb.c#L965 + slv->stlink_v = sl->q_buf[0]; + slv->swim_v = sl->q_buf[1]; + slv->jtag_v = sl->q_buf[2]; + slv->st_vid = (uint32_t)((sl->q_buf[9] << 8) | sl->q_buf[8]); + slv->stlink_pid = (uint32_t)((sl->q_buf[11] << 8) | sl->q_buf[10]); + slv->jtag_api = STLINK_JTAG_API_V3; + /* preferred API to get last R/W status */ + sl->version.flags |= STLINK_F_HAS_GETLASTRWSTATUS2; + sl->version.flags |= STLINK_F_HAS_TRACE; + sl->max_trace_freq = STLINK_V3_MAX_TRACE_FREQUENCY; + } + + return; +} + +void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { + stlink_write_reg(sl, addr, 15); /* pc register */ + stlink_run(sl, RUN_NORMAL); + + while (stlink_is_core_halted(sl)) { + usleep(3000000); + } +} + +static int32_t stlink_read(stlink_t *sl, stm32_addr_t addr, uint32_t size, save_block_fn fn, void *fn_arg) { + + int32_t error = -1; + + if (size < 1) { + size = sl->flash_size; + } + + if (size > sl->flash_size) { + size = sl->flash_size; + } + + uint32_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz; + + for (uint32_t off = 0; off < size; off += cmp_size) { + uint32_t aligned_size; + + // adjust last page size + if ((off + cmp_size) > size) { + cmp_size = size - off; + } + + aligned_size = cmp_size; + + if (aligned_size & (4 - 1)) { + aligned_size = (cmp_size + 4) & ~(4 - 1); + } + + stlink_read_mem32(sl, addr + off, (uint16_t)aligned_size); + + if (!fn(fn_arg, sl->q_buf, aligned_size)) { + goto on_error; + } + } + + error = 0; // success + +on_error: + return (error); +} + +static bool stlink_fread_worker(void *arg, uint8_t *block, ssize_t len) { + struct stlink_fread_worker_arg *the_arg = (struct stlink_fread_worker_arg *)arg; + + if (write(the_arg->fd, block, len) != len) { + fprintf(stderr, "write() != aligned_size\n"); + return (false); + } else { + return (true); + } +} + +// TODO: length not checked +static uint8_t stlink_parse_hex(const char *hex) { + uint8_t d[2]; + + for (int32_t i = 0; i < 2; ++i) { + char c = *(hex + i); + + if (c >= '0' && c <= '9') { + d[i] = c - '0'; + } else if (c >= 'A' && c <= 'F') { + d[i] = c - 'A' + 10; + } else if (c >= 'a' && c <= 'f') { + d[i] = c - 'a' + 10; + } else { + return (0); // error + } + } + + return ((d[0] << 4) | (d[1])); +} + +static bool stlink_fread_ihex_newsegment(struct stlink_fread_ihex_worker_arg *the_arg) { + uint32_t addr = the_arg->addr; + uint8_t sum = 2 + 4 + (uint8_t)((addr & 0xFF000000) >> 24) + + (uint8_t)((addr & 0x00FF0000) >> 16); + + if (17 != fprintf(the_arg->file, ":02000004%04X%02X\r\n", + (addr & 0xFFFF0000) >> 16, (uint8_t)(0x100 - sum))) { + return (false); + } + + the_arg->lba = (addr & 0xFFFF0000); + return (true); +} + +static bool stlink_fread_ihex_writeline(struct stlink_fread_ihex_worker_arg *the_arg) { + uint8_t count = the_arg->buf_pos; + + if (count == 0) { + return (true); + } + + uint32_t addr = the_arg->addr; + + if (the_arg->lba != (addr & 0xFFFF0000)) { // segment changed + if (!stlink_fread_ihex_newsegment(the_arg)) { + return (false); + } + } + + uint8_t sum = count + (uint8_t)((addr & 0x0000FF00) >> 8) + + (uint8_t)(addr & 0x000000FF); + + if (9 != fprintf(the_arg->file, ":%02X%04X00", count, (addr & 0x0000FFFF))) { + return (false); + } + + for (uint8_t i = 0; i < count; ++i) { + uint8_t b = the_arg->buf[i]; + sum += b; + + if (2 != fprintf(the_arg->file, "%02X", b)) { + return (false); + } + } + + if (4 != fprintf(the_arg->file, "%02X\r\n", (uint8_t)(0x100 - sum))) { + return (false); + } + + the_arg->addr += count; + the_arg->buf_pos = 0; + + return (true); +} + +static bool stlink_fread_ihex_init(struct stlink_fread_ihex_worker_arg *the_arg, + int32_t fd, stm32_addr_t addr) { + the_arg->file = fdopen(fd, "w"); + the_arg->addr = addr; + the_arg->lba = 0; + the_arg->buf_pos = 0; + + return (the_arg->file != NULL); +} + +static bool stlink_fread_ihex_worker(void *arg, uint8_t *block, ssize_t len) { + struct stlink_fread_ihex_worker_arg *the_arg = + (struct stlink_fread_ihex_worker_arg *)arg; + + for (ssize_t i = 0; i < len; ++i) { + if (the_arg->buf_pos == sizeof(the_arg->buf)) { // line is full + if (!stlink_fread_ihex_writeline(the_arg)) { + return (false); + } + } + + the_arg->buf[the_arg->buf_pos++] = block[i]; + } + + return (true); +} + +static bool stlink_fread_ihex_finalize(struct stlink_fread_ihex_worker_arg *the_arg) { + if (!stlink_fread_ihex_writeline(the_arg)) { + return (false); + } + + // FIXME: do we need the Start Linear Address? + + if (13 != fprintf(the_arg->file, ":00000001FF\r\n")) { // EoF + return (false); + } + + return (0 == fclose(the_arg->file)); +} |