Uwe Bonnes 61e237ec87 cmsis_dap: Always write full reportsize + 1 buffers
Expect short reads and other errors else
2021-04-21 21:50:04 +02:00

393 lines
11 KiB
C

/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2019-2021 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Modified from edbg.c
* Links between bmp and edbg
*
* https://arm-software.github.io/CMSIS_5/DAP/html/index.html
*/
#include "general.h"
#include "gdb_if.h"
#include "adiv5.h"
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <sys/time.h>
#include <hidapi.h>
#include <wchar.h>
#include "bmp_hosted.h"
#include "swdptap.h"
#include "dap.h"
#include "cmsis_dap.h"
#include "cl_utils.h"
#include "target.h"
#include "target_internal.h"
uint8_t dap_caps;
uint8_t mode;
/*- Variables ---------------------------------------------------------------*/
static hid_device *handle = NULL;
static uint8_t hid_buffer[1024 + 1];
static int report_size = 64 + 1; // TODO: read actual report size
static bool has_swd_sequence = false;
/* LPC845 Breakout Board Rev. 0 report invalid response with > 65 bytes */
int dap_init(bmp_info_t *info)
{
if (hid_init())
return -1;
int size = strlen(info->serial);
wchar_t serial[64] = {0}, *wc = serial;
for (int i = 0; i < size; i++)
*wc++ = info->serial[i];
*wc = 0;
/* Blacklist devices that do not work with 513 byte report length
* FIXME: Find a solution to decipher from the device.
*/
if ((info->vid == 0x1fc9) && (info->pid == 0x0132)) {
DEBUG_WARN("Blacklist\n");
report_size = 64;
}
handle = hid_open(info->vid, info->pid, (serial[0]) ? serial : NULL);
if (!handle)
return -1;
dap_disconnect();
size = dap_info(DAP_INFO_FW_VER, hid_buffer, sizeof(hid_buffer));
if (size) {
DEBUG_INFO("Ver %s, ", hid_buffer);
int major = -1, minor = -1, sub = -1;
if (sscanf((const char *)hid_buffer, "%d.%d.%d",
&major, &minor, &sub)) {
if (sub == -1) {
if (minor >= 10) {
minor /= 10;
sub = 0;
}
}
has_swd_sequence = ((major > 1 ) || ((major > 0 ) && (minor > 1)));
}
}
size = dap_info(DAP_INFO_CAPABILITIES, hid_buffer, sizeof(hid_buffer));
dap_caps = hid_buffer[0];
DEBUG_INFO("Cap (0x%2x): %s%s%s", dap_caps,
(dap_caps & 1)? "SWD" : "",
((dap_caps & 3) == 3) ? "/" : "",
(dap_caps & 2)? "JTAG" : "");
if (dap_caps & 4)
DEBUG_INFO(", SWO_UART");
if (dap_caps & 8)
DEBUG_INFO(", SWO_MANCHESTER");
if (dap_caps & 0x10)
DEBUG_INFO(", Atomic Cmds");
size = dap_info(DAP_INFO_PACKET_SIZE, hid_buffer, sizeof(hid_buffer));
if (size) {
report_size = hid_buffer[0];
DEBUG_INFO(", Reportsize %d", hid_buffer[0]);
}
DEBUG_INFO("\n");
return 0;
}
static void dap_dp_abort(ADIv5_DP_t *dp, uint32_t abort)
{
/* DP Write to Reg 0.*/
dap_write_reg(dp, ADIV5_DP_ABORT, abort);
}
static uint32_t dap_dp_error(ADIv5_DP_t *dp)
{
uint32_t ctrlstat = dap_read_reg(dp, ADIV5_DP_CTRLSTAT);
uint32_t err = ctrlstat &
(ADIV5_DP_CTRLSTAT_STICKYORUN | ADIV5_DP_CTRLSTAT_STICKYCMP |
ADIV5_DP_CTRLSTAT_STICKYERR | ADIV5_DP_CTRLSTAT_WDATAERR);
uint32_t clr = 0;
if(err & ADIV5_DP_CTRLSTAT_STICKYORUN)
clr |= ADIV5_DP_ABORT_ORUNERRCLR;
if(err & ADIV5_DP_CTRLSTAT_STICKYCMP)
clr |= ADIV5_DP_ABORT_STKCMPCLR;
if(err & ADIV5_DP_CTRLSTAT_STICKYERR)
clr |= ADIV5_DP_ABORT_STKERRCLR;
if(err & ADIV5_DP_CTRLSTAT_WDATAERR)
clr |= ADIV5_DP_ABORT_WDERRCLR;
dap_write_reg(dp, ADIV5_DP_ABORT, clr);
dp->fault = 0;
return err;
}
static uint32_t dap_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW,
uint16_t addr, uint32_t value)
{
bool APnDP = addr & ADIV5_APnDP;
uint32_t res = 0;
uint8_t reg = (addr & 0xc) | ((APnDP)? 1 : 0);
if (RnW) {
res = dap_read_reg(dp, reg);
}
else {
dap_write_reg(dp, reg, value);
}
return res;
}
static uint32_t dap_dp_read_reg(ADIv5_DP_t *dp, uint16_t addr)
{
uint32_t res = dap_dp_low_access(dp, ADIV5_LOW_READ, addr, 0);
DEBUG_PROBE("dp_read %04x %08" PRIx32 "\n", addr, res);
return res;
}
void dap_exit_function(void)
{
if (handle) {
dap_disconnect();
hid_close(handle);
}
}
int dbg_get_report_size(void)
{
return report_size;
}
int dbg_dap_cmd(uint8_t *data, int size, int rsize)
{
char cmd = data[0];
int res;
memset(hid_buffer, 0, report_size + 1);
memcpy(&hid_buffer[1], data, rsize);
DEBUG_WIRE("cmd : ");
for(int i = 0; (i < 16) && (i < rsize + 1); i++)
DEBUG_WIRE("%02x.", hid_buffer[i]);
DEBUG_WIRE("\n");
/* Write must be as long as we expect the result, at least
* for Dappermime 20210213 */
res = hid_write(handle, hid_buffer, report_size + 1);
if (res < 0) {
DEBUG_WARN( "Error: %ls\n", hid_error(handle));
exit(-1);
}
res = hid_read(handle, hid_buffer, report_size + 1);
if (res < 0) {
DEBUG_WARN( "debugger read(): %ls\n", hid_error(handle));
exit(-1);
}
DEBUG_WIRE("res %2d: ", res);
for(int i = 0; (i < 16) && (i < res + 1); i++)
DEBUG_WIRE("%02x.", hid_buffer[i]);
DEBUG_WIRE("\n");
if (hid_buffer[0] != cmd) {
DEBUG_WARN("cmd %02x invalid response received %02x\n",
cmd, hid_buffer[0]);
}
if (size)
memcpy(data, &hid_buffer[1], (size < res) ? size : res);
return res;
}
#define ALIGNOF(x) (((x) & 3) == 0 ? ALIGN_WORD : \
(((x) & 1) == 0 ? ALIGN_HALFWORD : ALIGN_BYTE))
static void dap_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len)
{
if (len == 0)
return;
enum align align = MIN(ALIGNOF(src), ALIGNOF(len));
DEBUG_WIRE("memread @ %" PRIx32 " len %ld, align %d , start: \n",
src, len, align);
if (((unsigned)(1 << align)) == len)
return dap_read_single(ap, dest, src, align);
/* One word transfer for every byte/halfword/word
* Total number of bytes in transfer*/
unsigned int max_size = (dbg_get_report_size() - 5) >> (2 - align);
while (len) {
dap_ap_mem_access_setup(ap, src, align);
/* Calculate length until next access setup is needed */
unsigned int blocksize = (src | 0x3ff) - src + 1;
if (blocksize > len)
blocksize = len;
while (blocksize) {
unsigned int transfersize = blocksize;
if (transfersize > max_size)
transfersize = max_size;
unsigned int res = dap_read_block(ap, dest, src, transfersize,
align);
if (res) {
DEBUG_WIRE("mem_read failed %02x\n", res);
ap->dp->fault = 1;
return;
}
blocksize -= transfersize;
len -= transfersize;
dest += transfersize;
src += transfersize;
}
}
DEBUG_WIRE("memread res last data %08" PRIx32 "\n", ((uint32_t*)dest)[-1]);
}
static void dap_mem_write_sized(
ADIv5_AP_t *ap, uint32_t dest, const void *src,
size_t len, enum align align)
{
if (len == 0)
return;
DEBUG_WIRE("memwrite @ %" PRIx32 " len %ld, align %d , %08x start: \n",
dest, len, align, *(uint32_t *)src);
if (((unsigned)(1 << align)) == len)
return dap_write_single(ap, dest, src, align);
unsigned int max_size = (dbg_get_report_size() - 5) >> (2 - align);
while (len) {
dap_ap_mem_access_setup(ap, dest, align);
unsigned int blocksize = (dest | 0x3ff) - dest + 1;
if (blocksize > len)
blocksize = len;
while (blocksize) {
unsigned int transfersize = blocksize;
if (transfersize > max_size)
transfersize = max_size;
unsigned int res = dap_write_block(ap, dest, src, transfersize,
align);
if (res) {
DEBUG_WARN("mem_write failed %02x\n", res);
ap->dp->fault = 1;
return;
}
blocksize -= transfersize;
len -= transfersize;
dest += transfersize;
src += transfersize;
}
}
DEBUG_WIRE("memwrite done\n");
}
int dap_enter_debug_swd(ADIv5_DP_t *dp)
{
dp->idcode = dap_read_idcode(dp);
dp->dp_read = dap_dp_read_reg;
dp->error = dap_dp_error;
dp->low_access = dap_dp_low_access;
dp->abort = dap_dp_abort; /* DP Write to Reg 0.*/
return 0;
}
void dap_adiv5_dp_defaults(ADIv5_DP_t *dp)
{
if ((mode == DAP_CAP_JTAG) && dap_jtag_configure())
return;
dp->ap_read = dap_ap_read;
dp->ap_write = dap_ap_write;
dp->mem_read = dap_mem_read;
dp->mem_write_sized = dap_mem_write_sized;
}
static void cmsis_dap_jtagtap_reset(void)
{
jtagtap_soft_reset();
/* Is there a way to know if TRST is available?*/
}
static void cmsis_dap_jtagtap_tms_seq(uint32_t MS, int ticks)
{
uint8_t TMS[4] = {MS & 0xff, (MS >> 8) & 0xff, (MS >> 16) & 0xff,
(MS >> 24) & 0xff};
dap_jtagtap_tdi_tdo_seq(NULL, false, TMS, NULL, ticks);
DEBUG_PROBE("tms_seq DI %08x %d\n", MS, ticks);
}
static void cmsis_dap_jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms,
const uint8_t *DI, int ticks)
{
dap_jtagtap_tdi_tdo_seq(DO, (final_tms), NULL, DI, ticks);
DEBUG_PROBE("jtagtap_tdi_tdo_seq %d, %02x-> %02x\n", ticks, DI[0], DO[0]);
}
static void cmsis_dap_jtagtap_tdi_seq(const uint8_t final_tms,
const uint8_t *DI, int ticks)
{
dap_jtagtap_tdi_tdo_seq(NULL, (final_tms), NULL, DI, ticks);
DEBUG_PROBE("jtagtap_tdi_seq %d, %02x\n", ticks, DI[0]);
}
static uint8_t cmsis_dap_jtagtap_next(uint8_t dTMS, uint8_t dTDI)
{
uint8_t tdo[1];
dap_jtagtap_tdi_tdo_seq(tdo, false, &dTMS, &dTDI, 1);
DEBUG_PROBE("next tms %02x tdi %02x tdo %02x\n", dTMS, dTDI, tdo[0]);
return (tdo[0] & 1);
}
int cmsis_dap_jtagtap_init(jtag_proc_t *jtag_proc)
{
DEBUG_PROBE("jtap_init\n");
if (!(dap_caps & DAP_CAP_JTAG))
return -1;
mode = DAP_CAP_JTAG;
dap_disconnect();
dap_connect(true);
dap_reset_link(true);
jtag_proc->jtagtap_reset = cmsis_dap_jtagtap_reset;
jtag_proc->jtagtap_next = cmsis_dap_jtagtap_next;
jtag_proc->jtagtap_tms_seq = cmsis_dap_jtagtap_tms_seq;
jtag_proc->jtagtap_tdi_tdo_seq = cmsis_dap_jtagtap_tdi_tdo_seq;
jtag_proc->jtagtap_tdi_seq = cmsis_dap_jtagtap_tdi_seq;
return 0;
}
int dap_jtag_dp_init(ADIv5_DP_t *dp)
{
dp->dp_read = dap_dp_read_reg;
dp->error = dap_dp_error;
dp->low_access = dap_dp_low_access;
dp->abort = dap_dp_abort;
return true;
}
int dap_swdptap_init(swd_proc_t *swd_proc)
{
if (!(dap_caps & DAP_CAP_SWD))
return 1;
mode = DAP_CAP_SWD;
dap_transfer_configure(2, 128, 128);
dap_swd_configure(0);
dap_connect(false);
dap_led(0, 1);
dap_reset_link(false);
if (has_swd_sequence) {
swd_proc->swdptap_seq_in = dap_swdptap_seq_in;
swd_proc->swdptap_seq_in_parity = dap_swdptap_seq_in_parity;
swd_proc->swdptap_seq_out = dap_swdptap_seq_out;
swd_proc->swdptap_seq_out_parity = dap_swdptap_seq_out_parity;
swd_proc->swdp_read = dap_dp_read_reg;
swd_proc->swdp_error = dap_dp_error;
swd_proc->swdp_low_access = dap_dp_low_access;
swd_proc->swdp_abort = dap_dp_abort;
}
return 0;
}