X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fstlink_usb.c;h=325848f8616df22c9a259625a7b4fad212786eac;hb=414c469cdaef60eeea02dc54a94eb904555cf35d;hp=92eb06d3ab028c201fae38f07e085d609565b773;hpb=76d109523199aa44bb36627ff46d91719566086a;p=fw%2Fopenocd diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 92eb06d3a..325848f86 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -33,9 +33,12 @@ #endif /* project specific includes */ +#include #include #include #include +#include +#include #include #include #include @@ -113,6 +116,10 @@ */ #define MAX_WAIT_RETRIES 8 +/* HLA is currently limited at AP#0 and no control on CSW */ +#define STLINK_HLA_AP_NUM 0 +#define STLINK_HLA_CSW 0 + enum stlink_jtag_api_version { STLINK_JTAG_API_V1 = 1, STLINK_JTAG_API_V2, @@ -149,6 +156,13 @@ struct stlink_usb_priv_s { struct libusb_transfer *trans; }; +struct stlink_tcp_version { + uint32_t api; + uint32_t major; + uint32_t minor; + uint32_t build; +}; + struct stlink_tcp_priv_s { /** */ int fd; @@ -162,6 +176,8 @@ struct stlink_tcp_priv_s { uint8_t *send_buf; /** */ uint8_t *recv_buf; + /** */ + struct stlink_tcp_version version; }; struct stlink_backend_s { @@ -182,10 +198,25 @@ struct stlink_backend_s { enum queue_cmd { CMD_DP_READ = 1, CMD_DP_WRITE, + CMD_AP_READ, CMD_AP_WRITE, + + /* + * encode the bytes size in the enum's value. This makes easy to extract it + * with a simple logic AND, by using the macro CMD_MEM_AP_2_SIZE() below + */ + CMD_MEM_AP_READ8 = 0x10 + 1, + CMD_MEM_AP_READ16 = 0x10 + 2, + CMD_MEM_AP_READ32 = 0x10 + 4, + + CMD_MEM_AP_WRITE8 = 0x20 + 1, + CMD_MEM_AP_WRITE16 = 0x20 + 2, + CMD_MEM_AP_WRITE32 = 0x20 + 4, }; +#define CMD_MEM_AP_2_SIZE(cmd) ((cmd) & 7) + struct dap_queue { enum queue_cmd cmd; union { @@ -208,7 +239,17 @@ struct dap_queue { unsigned int reg; struct adiv5_ap *ap; uint32_t data; + bool changes_csw_default; } ap_w; + struct mem_ap { + uint32_t addr; + struct adiv5_ap *ap; + union { + uint32_t *p_data; + uint32_t data; + }; + uint32_t csw; + } mem_ap; }; }; @@ -406,6 +447,12 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i #define STLINK_DEBUG_APIV2_INIT_AP 0x4B #define STLINK_DEBUG_APIV2_CLOSE_AP_DBG 0x4C +#define STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC 0x50 +#define STLINK_DEBUG_APIV2_RW_MISC_OUT 0x51 +#define STLINK_DEBUG_APIV2_RW_MISC_IN 0x52 + +#define STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC 0x54 + #define STLINK_APIV3_SET_COM_FREQ 0x61 #define STLINK_APIV3_GET_COM_FREQ 0x62 @@ -478,6 +525,10 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i /* aliases */ #define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE #define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2 +#define STLINK_F_HAS_MEM_WR_NO_INC STLINK_F_HAS_MEM_16BIT +#define STLINK_F_HAS_MEM_RD_NO_INC STLINK_F_HAS_DPBANKSEL +#define STLINK_F_HAS_RW_MISC STLINK_F_HAS_DPBANKSEL +#define STLINK_F_HAS_CSW STLINK_F_HAS_DPBANKSEL #define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F) @@ -886,17 +937,35 @@ static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool return ERROR_FAIL; } - keep_alive(); - /* read the TCP response */ - int received_size = recv(h->tcp_backend_priv.fd, (void *)h->tcp_backend_priv.recv_buf, recv_size, 0); - if (received_size != recv_size) { - LOG_ERROR("failed to receive USB CMD response"); - if (received_size == -1) + int retval = ERROR_OK; + int remaining_bytes = recv_size; + uint8_t *recv_buf = h->tcp_backend_priv.recv_buf; + const int64_t timeout = timeval_ms() + 1000; /* 1 second */ + + while (remaining_bytes > 0) { + if (timeval_ms() > timeout) { + LOG_DEBUG("received size %d (expected %d)", recv_size - remaining_bytes, recv_size); + retval = ERROR_TIMEOUT_REACHED; + break; + } + + keep_alive(); + int received = recv(h->tcp_backend_priv.fd, (void *)recv_buf, remaining_bytes, 0); + + if (received == -1) { LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); - else - LOG_DEBUG("received size %d (expected %d)", received_size, recv_size); - return ERROR_FAIL; + retval = ERROR_FAIL; + break; + } + + recv_buf += received; + remaining_bytes -= received; + } + + if (retval != ERROR_OK) { + LOG_ERROR("failed to receive USB CMD response"); + return retval; } if (check_tcp_status) { @@ -1294,6 +1363,7 @@ static int stlink_usb_version(void *handle) flags |= STLINK_F_QUIRK_JTAG_DP_READ; /* API to read/write memory at 16 bit from J26 */ + /* API to write memory without address increment from J26 */ if (h->version.jtag >= 26) flags |= STLINK_F_HAS_MEM_16BIT; @@ -1306,6 +1376,8 @@ static int stlink_usb_version(void *handle) flags |= STLINK_F_FIX_CLOSE_AP; /* Banked regs (DPv1 & DPv2) support from V2J32 */ + /* API to read memory without address increment from V2J32 */ + /* Memory R/W supports CSW from V2J32 */ if (h->version.jtag >= 32) flags |= STLINK_F_HAS_DPBANKSEL; @@ -1327,6 +1399,7 @@ static int stlink_usb_version(void *handle) flags |= STLINK_F_HAS_DAP_REG; /* API to read/write memory at 16 bit */ + /* API to write memory without address increment */ flags |= STLINK_F_HAS_MEM_16BIT; /* API required to init AP before any AP access */ @@ -1336,6 +1409,8 @@ static int stlink_usb_version(void *handle) flags |= STLINK_F_FIX_CLOSE_AP; /* Banked regs (DPv1 & DPv2) support from V3J2 */ + /* API to read memory without address increment from V3J2 */ + /* Memory R/W supports CSW from V3J2 */ if (h->version.jtag >= 2) flags |= STLINK_F_HAS_DPBANKSEL; @@ -2377,8 +2452,8 @@ static int stlink_usb_get_rw_status(void *handle) } /** */ -static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len, - uint8_t *buffer) +static int stlink_usb_read_mem8(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, uint8_t *buffer) { int res; uint16_t read_len = len; @@ -2386,6 +2461,9 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len, assert(handle); + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */ if (len > stlink_usb_block(h)) { LOG_DEBUG("max buffer (%d) length exceeded", stlink_usb_block(h)); @@ -2400,6 +2478,9 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; /* we need to fix read length for single bytes */ if (read_len == 1) @@ -2416,14 +2497,17 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len, } /** */ -static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len, - const uint8_t *buffer) +static int stlink_usb_write_mem8(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, const uint8_t *buffer) { int res; struct stlink_usb_handle_s *h = handle; assert(handle); + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */ if (len > stlink_usb_block(h)) { LOG_DEBUG("max buffer length (%d) exceeded", stlink_usb_block(h)); @@ -2438,6 +2522,9 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; res = stlink_usb_xfer_noerrcheck(handle, buffer, len); @@ -2448,8 +2535,8 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len, } /** */ -static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len, - uint8_t *buffer) +static int stlink_usb_read_mem16(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, uint8_t *buffer) { int res; struct stlink_usb_handle_s *h = handle; @@ -2459,6 +2546,9 @@ static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len, if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT)) return ERROR_COMMAND_NOTFOUND; + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + if (len > STLINK_MAX_RW16_32) { LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); return ERROR_FAIL; @@ -2478,6 +2568,9 @@ static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; res = stlink_usb_xfer_noerrcheck(handle, h->databuf, len); @@ -2490,8 +2583,8 @@ static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len, } /** */ -static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len, - const uint8_t *buffer) +static int stlink_usb_write_mem16(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, const uint8_t *buffer) { int res; struct stlink_usb_handle_s *h = handle; @@ -2501,6 +2594,9 @@ static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len, if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT)) return ERROR_COMMAND_NOTFOUND; + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + if (len > STLINK_MAX_RW16_32) { LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); return ERROR_FAIL; @@ -2520,6 +2616,9 @@ static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; res = stlink_usb_xfer_noerrcheck(handle, buffer, len); @@ -2530,14 +2629,17 @@ static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len, } /** */ -static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len, - uint8_t *buffer) +static int stlink_usb_read_mem32(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, uint8_t *buffer) { int res; struct stlink_usb_handle_s *h = handle; assert(handle); + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + if (len > STLINK_MAX_RW16_32) { LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); return ERROR_FAIL; @@ -2557,6 +2659,9 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; res = stlink_usb_xfer_noerrcheck(handle, h->databuf, len); @@ -2569,14 +2674,17 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len, } /** */ -static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len, - const uint8_t *buffer) +static int stlink_usb_write_mem32(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, const uint8_t *buffer) { int res; struct stlink_usb_handle_s *h = handle; assert(handle); + if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW)) + return ERROR_COMMAND_NOTFOUND; + if (len > STLINK_MAX_RW16_32) { LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); return ERROR_FAIL; @@ -2596,6 +2704,9 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len, h->cmdidx += 4; h_u16_to_le(h->cmdbuf+h->cmdidx, len); h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; res = stlink_usb_xfer_noerrcheck(handle, buffer, len); @@ -2605,6 +2716,88 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len, return stlink_usb_get_rw_status(handle); } +static int stlink_usb_read_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_MEM_RD_NO_INC)) + return ERROR_COMMAND_NOTFOUND; + + if (len > STLINK_MAX_RW16_32) { + LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); + return ERROR_FAIL; + } + + /* data must be a multiple of 4 and word aligned */ + if (len % 4 || addr % 4) { + LOG_DEBUG("Invalid data alignment"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + stlink_usb_init_buffer(handle, h->rx_ep, len); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC; + h_u32_to_le(h->cmdbuf + h->cmdidx, addr); + h->cmdidx += 4; + h_u16_to_le(h->cmdbuf + h->cmdidx, len); + h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; + + int retval = stlink_usb_xfer_noerrcheck(handle, h->databuf, len); + if (retval != ERROR_OK) + return retval; + + memcpy(buffer, h->databuf, len); + + return stlink_usb_get_rw_status(handle); +} + +static int stlink_usb_write_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint16_t len, const uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_MEM_WR_NO_INC)) + return ERROR_COMMAND_NOTFOUND; + + if (len > STLINK_MAX_RW16_32) { + LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32); + return ERROR_FAIL; + } + + /* data must be a multiple of 4 and word aligned */ + if (len % 4 || addr % 4) { + LOG_DEBUG("Invalid data alignment"); + return ERROR_TARGET_UNALIGNED_ACCESS; + } + + stlink_usb_init_buffer(handle, h->tx_ep, len); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC; + h_u32_to_le(h->cmdbuf + h->cmdidx, addr); + h->cmdidx += 4; + h_u16_to_le(h->cmdbuf + h->cmdidx, len); + h->cmdidx += 2; + h->cmdbuf[h->cmdidx++] = ap_num; + h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8); + h->cmdidx += 3; + + int retval = stlink_usb_xfer_noerrcheck(handle, buffer, len); + if (retval != ERROR_OK) + return retval; + + return stlink_usb_get_rw_status(handle); +} + static uint32_t stlink_max_block_size(uint32_t tar_autoincr_block, uint32_t address) { uint32_t max_tar_block = (tar_autoincr_block - ((tar_autoincr_block - 1) & address)); @@ -2613,8 +2806,8 @@ static uint32_t stlink_max_block_size(uint32_t tar_autoincr_block, uint32_t addr return max_tar_block; } -static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size, - uint32_t count, uint8_t *buffer) +static int stlink_usb_read_ap_mem(void *handle, uint8_t ap_num, uint32_t csw, + uint32_t addr, uint32_t size, uint32_t count, uint8_t *buffer) { int retval = ERROR_OK; uint32_t bytes_remaining; @@ -2629,7 +2822,6 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size, size = 1; while (count) { - bytes_remaining = (size != 1) ? stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h); @@ -2643,7 +2835,6 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size, * as 8bit access. */ if (size != 1) { - /* When in jtag mode the stlink uses the auto-increment functionality. * However it expects us to pass the data correctly, this includes * alignment and any page boundaries. We already do this as part of the @@ -2654,11 +2845,10 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size, /* we first need to check for any unaligned bytes */ if (addr & (size - 1)) { - uint32_t head_bytes = size - (addr & (size - 1)); - retval = stlink_usb_read_mem8(handle, addr, head_bytes, buffer); + retval = stlink_usb_read_mem8(handle, ap_num, csw, addr, head_bytes, buffer); if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) { - usleep((1<vid, param->pid, param->serial, + if (jtag_libusb_open(param->vid, param->pid, &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) { LOG_ERROR("open failed"); return ERROR_FAIL; @@ -3336,16 +3541,19 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) return ERROR_FAIL; } - uint32_t api_ver = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); - uint32_t ver_major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - uint32_t ver_minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); - uint32_t ver_build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); + h->tcp_backend_priv.version.api = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); + h->tcp_backend_priv.version.major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + h->tcp_backend_priv.version.minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); + h->tcp_backend_priv.version.build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); LOG_INFO("stlink-server API v%d, version %d.%d.%d", - api_ver, ver_major, ver_minor, ver_build); + h->tcp_backend_priv.version.api, + h->tcp_backend_priv.version.major, + h->tcp_backend_priv.version.minor, + h->tcp_backend_priv.version.build); /* in stlink-server API v1 sending more than 1428 bytes will cause stlink-server * to crash in windows: select a safe default value (1K) */ - if (api_ver < 2) + if (h->tcp_backend_priv.version.api < 2) h->max_mem_packet = (1 << 10); /* refresh stlink list (re-enumerate) */ @@ -3379,7 +3587,8 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) char serial[STLINK_TCP_SERIAL_SIZE + 1] = {0}; uint8_t stlink_used; bool stlink_id_matched = false; - bool stlink_serial_matched = (!param->serial); + const char *adapter_serial = adapter_get_required_serial(); + bool stlink_serial_matched = !adapter_serial; for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { /* get the stlink info */ @@ -3409,27 +3618,28 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) continue; /* check the serial if specified */ - if (param->serial) { + if (adapter_serial) { /* ST-Link server fixes the buggy serial returned by old ST-Link DFU * for further details refer to stlink_usb_get_alternate_serial * so if the user passes the buggy serial, we need to fix it before * comparing with the serial returned by ST-Link server */ - if (strlen(param->serial) == STLINK_SERIAL_LEN / 2) { + if (strlen(adapter_serial) == STLINK_SERIAL_LEN / 2) { char fixed_serial[STLINK_SERIAL_LEN + 1]; for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) - sprintf(fixed_serial + i, "%02X", param->serial[i / 2]); + sprintf(fixed_serial + i, "%02X", adapter_serial[i / 2]); fixed_serial[STLINK_SERIAL_LEN] = '\0'; stlink_serial_matched = strcmp(fixed_serial, serial) == 0; - } else - stlink_serial_matched = strcmp(param->serial, serial) == 0; + } else { + stlink_serial_matched = strcmp(adapter_serial, serial) == 0; + } } if (!stlink_serial_matched) LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", - serial, param->serial); + serial, adapter_serial); else /* exit the search loop if there is match */ break; } @@ -3498,7 +3708,7 @@ static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode for (unsigned i = 0; param->vid[i]; i++) { LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", h->st_mode, param->vid[i], param->pid[i], - param->serial ? param->serial : ""); + adapter_get_required_serial() ? adapter_get_required_serial() : ""); } if (param->use_stlink_tcp) @@ -3561,8 +3771,8 @@ static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode h->max_mem_packet = (1 << 10); uint8_t buffer[4]; - stlink_usb_open_ap(h, 0); - err = stlink_usb_read_mem32(h, CPUID, 4, buffer); + stlink_usb_open_ap(h, STLINK_HLA_AP_NUM); + err = stlink_usb_read_mem32(h, STLINK_HLA_AP_NUM, STLINK_HLA_CSW, CPUID, 4, buffer); if (err == ERROR_OK) { uint32_t cpuid = le_to_h_u32(buffer); int i = (cpuid >> 4) & 0xf; @@ -3695,6 +3905,53 @@ static int stlink_usb_close_access_port(void *handle, unsigned char ap_num) } +static int stlink_usb_rw_misc_out(void *handle, uint32_t items, const uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int buflen = ALIGN_UP(items, 4) + 4 * items; + + LOG_DEBUG_IO("%s(%" PRIu32 ")", __func__, items); + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return ERROR_COMMAND_NOTFOUND; + + stlink_usb_init_buffer(handle, h->tx_ep, buflen); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RW_MISC_OUT; + h_u32_to_le(&h->cmdbuf[2], items); + + return stlink_usb_xfer_noerrcheck(handle, buffer, buflen); +} + +static int stlink_usb_rw_misc_in(void *handle, uint32_t items, uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int buflen = 2 * 4 * items; + + LOG_DEBUG_IO("%s(%" PRIu32 ")", __func__, items); + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return ERROR_COMMAND_NOTFOUND; + + stlink_usb_init_buffer(handle, h->rx_ep, buflen); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RW_MISC_IN; + + int res = stlink_usb_xfer_noerrcheck(handle, h->databuf, buflen); + if (res != ERROR_OK) + return res; + + memcpy(buffer, h->databuf, buflen); + + return ERROR_OK; +} + /** */ static int stlink_read_dap_register(void *handle, unsigned short dap_port, unsigned short addr, uint32_t *val) @@ -3789,6 +4046,7 @@ struct hl_layout_api_s stlink_usb_layout_api = { static struct stlink_usb_handle_s *stlink_dap_handle; static struct hl_interface_param_s stlink_dap_param; static DECLARE_BITMAP(opened_ap, DP_APSEL_MAX + 1); +static uint32_t last_csw_default[DP_APSEL_MAX + 1]; static int stlink_dap_error = ERROR_OK; /** */ @@ -3833,6 +4091,7 @@ static int stlink_usb_open_ap(void *handle, unsigned short apsel) LOG_DEBUG("AP %d enabled", apsel); set_bit(apsel, opened_ap); + last_csw_default[apsel] = 0; return ERROR_OK; } @@ -3916,6 +4175,8 @@ static int stlink_dap_op_connect(struct adiv5_dap *dap) dap->do_reconnect = false; dap_invalidate_cache(dap); + for (unsigned int i = 0; i <= DP_APSEL_MAX; i++) + last_csw_default[i] = 0; retval = dap_dp_init(dap); if (retval != ERROR_OK) { @@ -4053,6 +4314,249 @@ static int stlink_dap_op_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) return ERROR_OK; } +#define RW_MISC_CMD_ADDRESS 1 +#define RW_MISC_CMD_WRITE 2 +#define RW_MISC_CMD_READ 3 +#define RW_MISC_CMD_APNUM 5 + +static int stlink_usb_misc_rw_segment(void *handle, const struct dap_queue *q, unsigned int len, unsigned int items) +{ + uint8_t buf[2 * 4 * items]; + + LOG_DEBUG("Queue: %u commands in %u items", len, items); + + int ap_num = DP_APSEL_INVALID; + unsigned int cmd_index = 0; + unsigned int val_index = ALIGN_UP(items, 4); + for (unsigned int i = 0; i < len; i++) { + if (ap_num != q[i].mem_ap.ap->ap_num) { + ap_num = q[i].mem_ap.ap->ap_num; + buf[cmd_index++] = RW_MISC_CMD_APNUM; + h_u32_to_le(&buf[val_index], ap_num); + val_index += 4; + } + + switch (q[i].cmd) { + case CMD_MEM_AP_READ32: + buf[cmd_index++] = RW_MISC_CMD_READ; + h_u32_to_le(&buf[val_index], q[i].mem_ap.addr); + val_index += 4; + break; + case CMD_MEM_AP_WRITE32: + buf[cmd_index++] = RW_MISC_CMD_ADDRESS; + h_u32_to_le(&buf[val_index], q[i].mem_ap.addr); + val_index += 4; + buf[cmd_index++] = RW_MISC_CMD_WRITE; + h_u32_to_le(&buf[val_index], q[i].mem_ap.data); + val_index += 4; + break; + default: + /* Not supposed to happen */ + return ERROR_FAIL; + } + } + /* pad after last command */ + while (!IS_ALIGNED(cmd_index, 4)) + buf[cmd_index++] = 0; + + int retval = stlink_usb_rw_misc_out(handle, items, buf); + if (retval != ERROR_OK) + return retval; + + retval = stlink_usb_rw_misc_in(handle, items, buf); + if (retval != ERROR_OK) + return retval; + + ap_num = DP_APSEL_INVALID; + val_index = 0; + unsigned int err_index = 4 * items; + for (unsigned int i = 0; i < len; i++) { + uint32_t errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + if (ap_num != q[i].mem_ap.ap->ap_num) { + ap_num = q[i].mem_ap.ap->ap_num; + err_index += 4; + val_index += 4; + errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + } + + if (q[i].cmd == CMD_MEM_AP_READ32) { + *q[i].mem_ap.p_data = le_to_h_u32(&buf[val_index]); + } else { /* q[i]->cmd == CMD_MEM_AP_WRITE32 */ + err_index += 4; + val_index += 4; + errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + } + err_index += 4; + val_index += 4; + } + + return ERROR_OK; +} + +static int stlink_usb_buf_rw_segment(void *handle, const struct dap_queue *q, unsigned int count) +{ + uint32_t bufsize = count * CMD_MEM_AP_2_SIZE(q[0].cmd); + uint8_t buf[bufsize]; + uint8_t ap_num = q[0].mem_ap.ap->ap_num; + uint32_t addr = q[0].mem_ap.addr; + uint32_t csw = q[0].mem_ap.csw; + + int retval = stlink_dap_open_ap(ap_num); + if (retval != ERROR_OK) + return retval; + + switch (q[0].cmd) { + case CMD_MEM_AP_WRITE8: + for (unsigned int i = 0; i < count; i++) + buf[i] = q[i].mem_ap.data >> 8 * (q[i].mem_ap.addr & 3); + return stlink_usb_write_mem8(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + + case CMD_MEM_AP_WRITE16: + for (unsigned int i = 0; i < count; i++) + h_u16_to_le(&buf[2 * i], q[i].mem_ap.data >> 8 * (q[i].mem_ap.addr & 2)); + return stlink_usb_write_mem16(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + + case CMD_MEM_AP_WRITE32: + for (unsigned int i = 0; i < count; i++) + h_u32_to_le(&buf[4 * i], q[i].mem_ap.data); + if (count > 1 && q[0].mem_ap.addr == q[1].mem_ap.addr) + return stlink_usb_write_mem32_noaddrinc(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + else + return stlink_usb_write_mem32(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + + case CMD_MEM_AP_READ8: + retval = stlink_usb_read_mem8(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + if (retval == ERROR_OK) + for (unsigned int i = 0; i < count; i++) + *q[i].mem_ap.p_data = buf[i] << 8 * (q[i].mem_ap.addr & 3); + return retval; + + case CMD_MEM_AP_READ16: + retval = stlink_usb_read_mem16(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + if (retval == ERROR_OK) + for (unsigned int i = 0; i < count; i++) + *q[i].mem_ap.p_data = le_to_h_u16(&buf[2 * i]) << 8 * (q[i].mem_ap.addr & 2); + return retval; + + case CMD_MEM_AP_READ32: + if (count > 1 && q[0].mem_ap.addr == q[1].mem_ap.addr) + retval = stlink_usb_read_mem32_noaddrinc(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + else + retval = stlink_usb_read_mem32(stlink_dap_handle, ap_num, csw, addr, bufsize, buf); + if (retval == ERROR_OK) + for (unsigned int i = 0; i < count; i++) + *q[i].mem_ap.p_data = le_to_h_u32(&buf[4 * i]); + return retval; + + default: + return ERROR_FAIL; + }; +} + +/* TODO: recover these values with cmd STLINK_DEBUG_APIV2_RW_MISC_GET_MAX (0x53) */ +#define STLINK_V2_RW_MISC_SIZE (64) +#define STLINK_V3_RW_MISC_SIZE (1227) + +static int stlink_usb_count_misc_rw_queue(void *handle, const struct dap_queue *q, unsigned int len, + unsigned int *pkt_items) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int i, items = 0; + int ap_num = DP_APSEL_INVALID; + unsigned int misc_max_items = (h->version.stlink == 2) ? STLINK_V2_RW_MISC_SIZE : STLINK_V3_RW_MISC_SIZE; + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return 0; + /* + * Before stlink-server API v3, RW_MISC sequence doesn't lock the st-link, + * so are not safe in shared mode. + * Don't use it with TCP backend to prevent any issue in case of sharing. + * This further degrades the performance, on top of TCP server overhead. + */ + if (h->backend == &stlink_tcp_backend && h->tcp_backend_priv.version.api < 3) + return 0; + + for (i = 0; i < len; i++) { + if (q[i].cmd != CMD_MEM_AP_READ32 && q[i].cmd != CMD_MEM_AP_WRITE32) + break; + unsigned int count = 1; + if (ap_num != q[i].mem_ap.ap->ap_num) { + count++; + ap_num = q[i].mem_ap.ap->ap_num; + } + if (q[i].cmd == CMD_MEM_AP_WRITE32) + count++; + if (items + count > misc_max_items) + break; + items += count; + } + + *pkt_items = items; + + return i; +} + +static int stlink_usb_count_buf_rw_queue(const struct dap_queue *q, unsigned int len) +{ + uint32_t incr = CMD_MEM_AP_2_SIZE(q[0].cmd); + unsigned int len_max; + + if (incr == 1) + len_max = stlink_usb_block(stlink_dap_handle); + else + len_max = STLINK_MAX_RW16_32 / incr; + + /* check for no address increment, 32 bits only */ + if (len > 1 && incr == 4 && q[0].mem_ap.addr == q[1].mem_ap.addr) + incr = 0; + + if (len > len_max) + len = len_max; + + for (unsigned int i = 1; i < len; i++) + if (q[i].cmd != q[0].cmd || + q[i].mem_ap.ap != q[0].mem_ap.ap || + q[i].mem_ap.csw != q[0].mem_ap.csw || + q[i].mem_ap.addr != q[i - 1].mem_ap.addr + incr) + return i; + + return len; +} + +static int stlink_usb_mem_rw_queue(void *handle, const struct dap_queue *q, unsigned int len, unsigned int *skip) +{ + unsigned int count, misc_items = 0; + int retval; + + unsigned int count_misc = stlink_usb_count_misc_rw_queue(handle, q, len, &misc_items); + unsigned int count_buf = stlink_usb_count_buf_rw_queue(q, len); + + if (count_misc > count_buf) { + count = count_misc; + retval = stlink_usb_misc_rw_segment(handle, q, count, misc_items); + } else { + count = count_buf; + retval = stlink_usb_buf_rw_segment(handle, q, count_buf); + } + if (retval != ERROR_OK) + return retval; + + *skip = count; + return ERROR_OK; +} + static void stlink_dap_run_internal(struct adiv5_dap *dap) { int retval = stlink_dap_check_reconnect(dap); @@ -4066,6 +4570,8 @@ static void stlink_dap_run_internal(struct adiv5_dap *dap) struct dap_queue *q = &stlink_dap_handle->queue[0]; while (i && stlink_dap_get_error() == ERROR_OK) { + unsigned int skip = 1; + switch (q->cmd) { case CMD_DP_READ: retval = stlink_dap_dp_read(q->dp_r.dap, q->dp_r.reg, q->dp_r.p_data); @@ -4077,16 +4583,29 @@ static void stlink_dap_run_internal(struct adiv5_dap *dap) retval = stlink_dap_ap_read(q->ap_r.ap, q->ap_r.reg, q->ap_r.p_data); break; case CMD_AP_WRITE: + /* ignore increment packed, not supported */ + if (q->ap_w.reg == MEM_AP_REG_CSW) + q->ap_w.data &= ~CSW_ADDRINC_PACKED; retval = stlink_dap_ap_write(q->ap_w.ap, q->ap_w.reg, q->ap_w.data); break; + + case CMD_MEM_AP_READ8: + case CMD_MEM_AP_READ16: + case CMD_MEM_AP_READ32: + case CMD_MEM_AP_WRITE8: + case CMD_MEM_AP_WRITE16: + case CMD_MEM_AP_WRITE32: + retval = stlink_usb_mem_rw_queue(stlink_dap_handle, q, i, &skip); + break; + default: LOG_ERROR("ST-Link: Unknown queue command %d", q->cmd); retval = ERROR_FAIL; break; } stlink_dap_record_error(retval); - q++; - i--; + q += skip; + i -= skip; } stlink_dap_handle->queue_index = 0; @@ -4206,10 +4725,57 @@ static int stlink_dap_op_queue_ap_read(struct adiv5_ap *ap, unsigned int reg, unsigned int i = stlink_dap_handle->queue_index++; struct dap_queue *q = &stlink_dap_handle->queue[i]; - q->cmd = CMD_AP_READ; - q->ap_r.reg = reg; - q->ap_r.ap = ap; - q->ap_r.p_data = data; + + /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_RD_NO_INC + * and STLINK_F_HAS_RW_MISC */ + if ((stlink_dap_handle->version.flags & STLINK_F_HAS_CSW) && + (reg == MEM_AP_REG_DRW || reg == MEM_AP_REG_BD0 || reg == MEM_AP_REG_BD1 || + reg == MEM_AP_REG_BD2 || reg == MEM_AP_REG_BD3)) { + /* de-queue previous write-TAR */ + struct dap_queue *prev_q = q - 1; + if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_TAR) { + stlink_dap_handle->queue_index = i; + i--; + q = prev_q; + prev_q--; + } + /* de-queue previous write-CSW if it didn't changed ap->csw_default */ + if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_CSW && + !prev_q->ap_w.changes_csw_default) { + stlink_dap_handle->queue_index = i; + q = prev_q; + } + + switch (ap->csw_value & CSW_SIZE_MASK) { + case CSW_8BIT: + q->cmd = CMD_MEM_AP_READ8; + break; + case CSW_16BIT: + q->cmd = CMD_MEM_AP_READ16; + break; + case CSW_32BIT: + q->cmd = CMD_MEM_AP_READ32; + break; + default: + LOG_ERROR("ST-Link: Unsupported CSW size %d", ap->csw_value & CSW_SIZE_MASK); + stlink_dap_record_error(ERROR_FAIL); + return ERROR_FAIL; + } + + q->mem_ap.addr = (reg == MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c)); + q->mem_ap.ap = ap; + q->mem_ap.p_data = data; + q->mem_ap.csw = ap->csw_default; + + /* force TAR and CSW update */ + ap->tar_valid = false; + ap->csw_value = 0; + } else { + q->cmd = CMD_AP_READ; + q->ap_r.reg = reg; + q->ap_r.ap = ap; + q->ap_r.p_data = data; + } if (i == MAX_QUEUE_DEPTH - 1) stlink_dap_run_internal(ap->dap); @@ -4225,10 +4791,63 @@ static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned int reg, unsigned int i = stlink_dap_handle->queue_index++; struct dap_queue *q = &stlink_dap_handle->queue[i]; - q->cmd = CMD_AP_WRITE; - q->ap_w.reg = reg; - q->ap_w.ap = ap; - q->ap_w.data = data; + + /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_WR_NO_INC + * and STLINK_F_HAS_RW_MISC */ + if ((stlink_dap_handle->version.flags & STLINK_F_HAS_CSW) && + (reg == MEM_AP_REG_DRW || reg == MEM_AP_REG_BD0 || reg == MEM_AP_REG_BD1 || + reg == MEM_AP_REG_BD2 || reg == MEM_AP_REG_BD3)) { + /* de-queue previous write-TAR */ + struct dap_queue *prev_q = q - 1; + if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_TAR) { + stlink_dap_handle->queue_index = i; + i--; + q = prev_q; + prev_q--; + } + /* de-queue previous write-CSW if it didn't changed ap->csw_default */ + if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_CSW && + !prev_q->ap_w.changes_csw_default) { + stlink_dap_handle->queue_index = i; + q = prev_q; + } + + switch (ap->csw_value & CSW_SIZE_MASK) { + case CSW_8BIT: + q->cmd = CMD_MEM_AP_WRITE8; + break; + case CSW_16BIT: + q->cmd = CMD_MEM_AP_WRITE16; + break; + case CSW_32BIT: + q->cmd = CMD_MEM_AP_WRITE32; + break; + default: + LOG_ERROR("ST-Link: Unsupported CSW size %d", ap->csw_value & CSW_SIZE_MASK); + stlink_dap_record_error(ERROR_FAIL); + return ERROR_FAIL; + } + + q->mem_ap.addr = (reg == MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c)); + q->mem_ap.ap = ap; + q->mem_ap.data = data; + q->mem_ap.csw = ap->csw_default; + + /* force TAR and CSW update */ + ap->tar_valid = false; + ap->csw_value = 0; + } else { + q->cmd = CMD_AP_WRITE; + q->ap_w.reg = reg; + q->ap_w.ap = ap; + q->ap_w.data = data; + if (reg == MEM_AP_REG_CSW && ap->csw_default != last_csw_default[ap->ap_num]) { + q->ap_w.changes_csw_default = true; + last_csw_default[ap->ap_num] = ap->csw_default; + } else { + q->ap_w.changes_csw_default = false; + } + } if (i == MAX_QUEUE_DEPTH - 1) stlink_dap_run_internal(ap->dap); @@ -4313,25 +4932,6 @@ static int stlink_dap_trace_read(uint8_t *buf, size_t *size) return stlink_usb_trace_read(stlink_dap_handle, buf, size); } -/** */ -COMMAND_HANDLER(stlink_dap_serial_command) -{ - LOG_DEBUG("stlink_dap_serial_command"); - - if (CMD_ARGC != 1) { - LOG_ERROR("Expected exactly one argument for \"st-link serial \"."); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - if (stlink_dap_param.serial) { - LOG_WARNING("Command \"st-link serial\" already used. Replacing previous value"); - free((void *)stlink_dap_param.serial); - } - - stlink_dap_param.serial = strdup(CMD_ARGV[0]); - return ERROR_OK; -} - /** */ COMMAND_HANDLER(stlink_dap_vid_pid) { @@ -4422,13 +5022,6 @@ COMMAND_HANDLER(stlink_dap_cmd_command) /** */ static const struct command_registration stlink_dap_subcommand_handlers[] = { - { - .name = "serial", - .handler = stlink_dap_serial_command, - .mode = COMMAND_CONFIG, - .help = "set the serial number of the adapter", - .usage = "", - }, { .name = "vid_pid", .handler = stlink_dap_vid_pid, @@ -4509,9 +5102,6 @@ static int stlink_dap_quit(void) { LOG_DEBUG("stlink_dap_quit()"); - free((void *)stlink_dap_param.serial); - stlink_dap_param.serial = NULL; - return stlink_close(stlink_dap_handle); }