kopia lustrzana https://github.com/stlink-org/stlink
1373 wiersze
36 KiB
C
1373 wiersze
36 KiB
C
/* == 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));
|
||
}
|