blackmagic/src/remote.c

470 lines
12 KiB
C

/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2019 Black Sphere Technologies Ltd.
* Written by Dave Marples <dave@marples.net>
* Modified 2020 - 2021 by 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/>.
*/
#include "general.h"
#include "remote.h"
#include "gdb_packet.h"
#include "jtagtap.h"
#include "gdb_if.h"
#include "version.h"
#include "exception.h"
#include <stdarg.h>
#include "target/adiv5.h"
#include "target.h"
#include "hex_utils.h"
#define NTOH(x) ((x<=9)?x+'0':'a'+x-10)
#define HTON(x) ((x<='9')?x-'0':((TOUPPER(x))-'A'+10))
#define TOUPPER(x) ((((x)>='a') && ((x)<='z'))?((x)-('a'-'A')):(x))
#define ISHEX(x) ( \
(((x)>='0') && ((x)<='9')) || \
(((x)>='A') && ((x)<='F')) || \
(((x)>='a') && ((x)<='f')) \
)
uint64_t remotehston(uint32_t limit, char *s)
/* Return numeric version of string, until illegal hex digit, or limit */
{
uint64_t ret=0L;
char c;
while (limit--) {
c=*s++;
if (!ISHEX(c))
return ret;
ret=(ret<<4)|HTON(c);
}
return ret;
}
#if PC_HOSTED == 0
static void remote_send_buf(uint8_t *buffer, size_t len)
{
uint8_t *p = buffer;
char hex[2];
do {
hexify(hex, (const void *)p++, 1);
gdb_if_putchar(hex[0], 0);
gdb_if_putchar(hex[1], 0);
} while (p < (buffer + len));
}
static void remote_respond_buf(char respCode, uint8_t *buffer, size_t len)
{
gdb_if_putchar(REMOTE_RESP, 0);
gdb_if_putchar(respCode, 0);
remote_send_buf(buffer, len);
gdb_if_putchar(REMOTE_EOM, 1);
}
/* Send response to far end */
static void remote_respond(char respCode, uint64_t param)
{
char buf[35]; /*Response, code, EOM and 2*16 hex nibbles*/
char *p = buf;
gdb_if_putchar(REMOTE_RESP, 0);
gdb_if_putchar(respCode, 0);
do {
*p++ = NTOH((param & 0x0f));
param >>= 4;
} while (param);
/* At this point the number to print is the buf, but backwards, so spool it out */
do {
gdb_if_putchar(*--p, 0);
} while (p > buf);
gdb_if_putchar(REMOTE_EOM, 1);
}
static void remote_respond_string(char respCode, const char *s)
/* Send response to far end */
{
gdb_if_putchar(REMOTE_RESP, 0);
gdb_if_putchar(respCode, 0);
while (*s) {
/* Just clobber illegal characters so they don't disturb the protocol */
if ((*s == '$') || (*s == REMOTE_SOM) || (*s == REMOTE_EOM))
gdb_if_putchar(' ', 0);
else
gdb_if_putchar(*s, 0);
s++;
}
gdb_if_putchar(REMOTE_EOM, 1);
}
static ADIv5_DP_t remote_dp = {
.ap_read = firmware_ap_read,
.ap_write = firmware_ap_write,
.mem_read = firmware_mem_read,
.mem_write_sized = firmware_mem_write_sized,
};
static void remotePacketProcessSWD(unsigned i, char *packet)
{
uint8_t ticks;
uint32_t param;
bool badParity;
switch (packet[1]) {
case REMOTE_INIT: /* SS = initialise =============================== */
if (i==2) {
remote_dp.dp_read = firmware_swdp_read;
remote_dp.low_access = firmware_swdp_low_access;
remote_dp.abort = firmware_swdp_abort;
swdptap_init(&remote_dp);
remote_respond(REMOTE_RESP_OK, 0);
} else {
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN);
}
break;
case REMOTE_IN_PAR: /* SI = In parity ============================= */
ticks=remotehston(2,&packet[2]);
badParity = remote_dp.seq_in_parity(&param, ticks);
remote_respond(badParity?REMOTE_RESP_PARERR:REMOTE_RESP_OK,param);
break;
case REMOTE_IN: /* Si = In ======================================= */
ticks=remotehston(2,&packet[2]);
param = remote_dp.seq_in(ticks);
remote_respond(REMOTE_RESP_OK,param);
break;
case REMOTE_OUT: /* So= Out ====================================== */
ticks=remotehston(2,&packet[2]);
param=remotehston(-1, &packet[4]);
remote_dp.seq_out(param, ticks);
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_OUT_PAR: /* SO = Out parity ========================== */
ticks=remotehston(2,&packet[2]);
param=remotehston(-1, &packet[4]);
remote_dp.seq_out_parity(param, ticks);
remote_respond(REMOTE_RESP_OK, 0);
break;
default:
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED);
break;
}
}
static void remotePacketProcessJTAG(unsigned i, char *packet)
{
uint32_t MS;
uint64_t DO;
size_t ticks;
uint64_t DI;
jtag_dev_t jtag_dev;
switch (packet[1]) {
case REMOTE_INIT: /* JS = initialise ============================= */
remote_dp.dp_read = fw_adiv5_jtagdp_read;
remote_dp.low_access = fw_adiv5_jtagdp_low_access;
remote_dp.abort = adiv5_jtagdp_abort;
jtagtap_init();
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_RESET: /* JR = reset ================================= */
jtag_proc.jtagtap_reset();
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_TMS: /* JT = TMS Sequence ============================ */
ticks=remotehston(2,&packet[2]);
MS=remotehston(2,&packet[4]);
if (i<4) {
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN);
} else {
jtag_proc.jtagtap_tms_seq( MS, ticks);
remote_respond(REMOTE_RESP_OK, 0);
}
break;
case REMOTE_CYCLE: { /* JC = clock cycle ============================ */
ticks = remotehston(8, &packet[4]);
const bool tms = packet[2] != '0';
const bool tdi = packet[3] != '0';
jtag_proc.jtagtap_cycle(tms, tdi, ticks);
remote_respond(REMOTE_RESP_OK, 0);
break;
}
case REMOTE_TDITDO_TMS: /* JD = TDI/TDO ========================================= */
case REMOTE_TDITDO_NOTMS:
if (i<5) {
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN);
} else {
ticks=remotehston(2,&packet[2]);
DI=remotehston(-1,&packet[4]);
jtag_proc.jtagtap_tdi_tdo_seq((void *)&DO, (packet[1]==REMOTE_TDITDO_TMS), (void *)&DI, ticks);
/* Mask extra bits on return value... */
if (ticks < 64)
DO &= (1LL << ticks) - 1;
remote_respond(REMOTE_RESP_OK, DO);
}
break;
case REMOTE_NEXT: /* JN = NEXT ======================================== */
if (i != 4)
remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN);
else {
const bool tdo = jtag_proc.jtagtap_next(packet[2] == '1', packet[3] == '1');
remote_respond(REMOTE_RESP_OK, tdo ? 1U : 0U);
}
break;
case REMOTE_ADD_JTAG_DEV: /* JJ = fill firmware jtag_devs */
if (i < 22) {
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_WRONGLEN);
} else {
memset(&jtag_dev, 0, sizeof(jtag_dev));
const uint32_t index = remotehston(2, &packet[ 2]);
jtag_dev.dr_prescan = remotehston(2, &packet[ 4]);
jtag_dev.dr_postscan = remotehston(2, &packet[ 6]);
jtag_dev.ir_len = remotehston(2, &packet[ 8]);
jtag_dev.ir_prescan = remotehston(2, &packet[10]);
jtag_dev.ir_postscan = remotehston(2, &packet[12]);
jtag_dev.current_ir = remotehston(8, &packet[14]);
jtag_add_device(index, &jtag_dev);
remote_respond(REMOTE_RESP_OK, 0);
}
break;
default:
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED);
break;
}
}
static void remotePacketProcessGEN(unsigned i, char *packet)
{
(void)i;
uint32_t freq;
switch (packet[1]) {
case REMOTE_VOLTAGE:
remote_respond_string(REMOTE_RESP_OK, platform_target_voltage());
break;
case REMOTE_NRST_SET:
platform_nrst_set_val(packet[2] == '1');
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_NRST_GET:
remote_respond(REMOTE_RESP_OK, platform_nrst_get_val());
break;
case REMOTE_FREQ_SET:
platform_max_frequency_set( remotehston(8, packet + 2));
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_FREQ_GET:
freq = platform_max_frequency_get();
remote_respond_buf(REMOTE_RESP_OK, (uint8_t*)&freq, 4);
break;
case REMOTE_PWR_SET:
#ifdef PLATFORM_HAS_POWER_SWITCH
if (packet[2]=='1'
&& !platform_target_get_power()
&& platform_target_voltage_sense() > POWER_CONFLICT_THRESHOLD) {
/* want to enable target power, but voltage > 0.5V sensed
* on the pin -> cancel
*/
remote_respond(REMOTE_RESP_ERR, 0);
} else {
platform_target_set_power(packet[2] == '1');
remote_respond(REMOTE_RESP_OK, 0);
}
#else
remote_respond(REMOTE_RESP_NOTSUP, 0);
#endif
break;
case REMOTE_PWR_GET:
#ifdef PLATFORM_HAS_POWER_SWITCH
remote_respond(REMOTE_RESP_OK, platform_target_get_power());
#else
remote_respond(REMOTE_RESP_NOTSUP, 0);
#endif
break;
#if !defined(BOARD_IDENT) && defined(BOARD_IDENT)
# define PLATFORM_IDENT() BOARD_IDENT
#endif
case REMOTE_START:
remote_respond_string(REMOTE_RESP_OK, PLATFORM_IDENT "" FIRMWARE_VERSION);
break;
default:
remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_UNRECOGNISED);
break;
}
}
static void remotePacketProcessHL(unsigned i, char *packet)
{
(void)i;
SET_IDLE_STATE(0);
ADIv5_AP_t remote_ap;
/* Re-use packet buffer. Align to DWORD! */
void *src = (void *)(((uint32_t)packet + 7) & ~7);
char index = packet[1];
if (index == REMOTE_HL_CHECK) {
remote_respond(REMOTE_RESP_OK, REMOTE_HL_VERSION);
return;
}
packet += 2;
remote_dp.dp_jd_index = remotehston(2, packet);
packet += 2;
remote_ap.apsel = remotehston(2, packet);
remote_ap.dp = &remote_dp;
switch (index) {
case REMOTE_DP_READ: /* Hd = Read from DP register */
packet += 2;
uint16_t addr16 = remotehston(4, packet);
uint32_t data = adiv5_dp_read(&remote_dp, addr16);
remote_respond_buf(REMOTE_RESP_OK, (uint8_t*)&data, 4);
break;
case REMOTE_LOW_ACCESS: /* HL = Low level access */
packet += 2;
addr16 = remotehston(4, packet);
packet += 4;
uint32_t value = remotehston(8, packet);
data = remote_dp.low_access(&remote_dp, remote_ap.apsel, addr16, value);
remote_respond_buf(REMOTE_RESP_OK, (uint8_t*)&data, 4);
break;
case REMOTE_AP_READ: /* Ha = Read from AP register*/
packet += 2;
addr16 = remotehston(4, packet);
data = adiv5_ap_read(&remote_ap, addr16);
remote_respond_buf(REMOTE_RESP_OK, (uint8_t*)&data, 4);
break;
case REMOTE_AP_WRITE: /* Ha = Write to AP register*/
packet += 2;
addr16 = remotehston(4, packet);
packet += 4;
value = remotehston(8, packet);
adiv5_ap_write(&remote_ap, addr16, value);
remote_respond(REMOTE_RESP_OK, 0);
break;
case REMOTE_AP_MEM_READ: /* HM = Read from Mem and set csw */
packet += 2;
remote_ap.csw = remotehston(8, packet);
packet += 6;
/*fall through*/
case REMOTE_MEM_READ: /* Hh = Read from Mem */
packet += 2;
uint32_t address = remotehston(8, packet);
packet += 8;
uint32_t count = remotehston(8, packet);
packet += 8;
adiv5_mem_read(&remote_ap, src, address, count);
if (remote_ap.dp->fault == 0) {
remote_respond_buf(REMOTE_RESP_OK, src, count);
break;
}
remote_respond(REMOTE_RESP_ERR, 0);
remote_ap.dp->fault = 0;
break;
case REMOTE_AP_MEM_WRITE_SIZED: /* Hm = Write to memory and set csw */
packet += 2;
remote_ap.csw = remotehston(8, packet);
packet += 6;
/*fall through*/
case REMOTE_MEM_WRITE_SIZED: /* HH = Write to memory*/
packet += 2;
enum align align = remotehston(2, packet);
packet += 2;
uint32_t dest = remotehston(8, packet);
packet+= 8;
size_t len = remotehston(8, packet);
packet += 8;
if (len & ((1 << align) - 1)) {
/* len and align do not fit*/
remote_respond(REMOTE_RESP_ERR, 0);
break;
}
/* Read as stream of hexified bytes*/
unhexify(src, packet, len);
adiv5_mem_write_sized(&remote_ap, dest, src, len, align);
if (remote_ap.dp->fault) {
/* Errors handles on hosted side.*/
remote_respond(REMOTE_RESP_ERR, 0);
remote_ap.dp->fault = 0;
break;
}
remote_respond(REMOTE_RESP_OK, 0);
break;
default:
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED);
break;
}
SET_IDLE_STATE(1);
}
void remotePacketProcess(unsigned i, char *packet)
{
switch (packet[0]) {
case REMOTE_SWDP_PACKET:
remotePacketProcessSWD(i,packet);
break;
case REMOTE_JTAG_PACKET:
remotePacketProcessJTAG(i,packet);
break;
case REMOTE_GEN_PACKET:
remotePacketProcessGEN(i,packet);
break;
case REMOTE_HL_PACKET:
remotePacketProcessHL(i, packet);
break;
default: /* Oh dear, unrecognised, return an error */
remote_respond(REMOTE_RESP_ERR,REMOTE_ERROR_UNRECOGNISED);
break;
}
}
#endif