#include <string.h>
#include "dap.h"
#include "dap_command.h"
#include "exception.h"
#include "buffer_utils.h"
#define DAP_TRANSFER_APnDP (1U << 0U)
#define DAP_TRANSFER_RnW (1U << 1U)
#define DAP_TRANSFER_A2 (1U << 2U)
#define DAP_TRANSFER_A3 (1U << 3U)
#define DAP_TRANSFER_MATCH_VALUE (1U << 4U)
#define DAP_TRANSFER_MATCH_MASK (1U << 5U)
#define DAP_JTAG_TMS_SET (1U << 6U)
#define DAP_JTAG_TMS_CLEAR (0U << 6U)
#define DAP_JTAG_TDO_CAPTURE (1U << 7U)
static size_t dap_encode_transfer(
const dap_transfer_request_s *const transfer, uint8_t *const buffer, const size_t offset)
{
buffer[offset] = transfer->request;
const uint8_t request_flags = transfer->request & (DAP_TRANSFER_RnW | DAP_TRANSFER_MATCH_VALUE);
if ((request_flags & DAP_TRANSFER_RnW) && !(request_flags & DAP_TRANSFER_MATCH_VALUE))
return 1U;
write_le4(buffer, offset + 1U, transfer->data);
return 5U;
}
static void dap_dispatch_status(adiv5_debug_port_s *const dp, const dap_transfer_status_e status)
{
switch (status) {
case DAP_TRANSFER_OK:
break;
case DAP_TRANSFER_WAIT:
dp->fault = status;
break;
case DAP_TRANSFER_FAULT:
DEBUG_ERROR("Access resulted in fault\n");
dp->fault = status;
break;
case DAP_TRANSFER_NO_RESPONSE:
DEBUG_ERROR("Access resulted in no response\n");
dp->fault = status;
break;
default:
DEBUG_ERROR("Access has invalid ack %x\n", status);
raise_exception(EXCEPTION_ERROR, "Invalid ACK");
break;
}
}
bool perform_dap_transfer(adiv5_debug_port_s *const target_dp, const dap_transfer_request_s *const transfer_requests,
const size_t requests, uint32_t *const response_data, const size_t responses)
{
if (!requests || requests > 12 || (responses && !response_data))
return false;
DEBUG_PROBE("-> dap_transfer (%zu requests)\n", requests);
uint8_t request[63] = {
DAP_TRANSFER,
target_dp->dev_index,
requests,
};
size_t offset = 3U;
for (size_t i = 0; i < requests; ++i)
offset += dap_encode_transfer(&transfer_requests[i], request, offset);
dap_transfer_response_s response = {.processed = 0, .status = DAP_TRANSFER_OK};
if (!dap_run_cmd(request, offset, &response, 2U + (responses * 4U))) {
dap_dispatch_status(target_dp, response.status);
return false;
}
if (response.processed == requests && response.status == DAP_TRANSFER_OK) {
for (size_t i = 0; i < responses; ++i)
response_data[i] = read_le4(response.data[i], 0);
return true;
}
DEBUG_PROBE("-> transfer failed with %u after processing %u requests\n", response.status, response.processed);
dap_dispatch_status(target_dp, response.status);
return false;
}
bool perform_dap_transfer_recoverable(adiv5_debug_port_s *const target_dp,
const dap_transfer_request_s *const transfer_requests, const size_t requests, uint32_t *const response_data,
const size_t responses)
{
const bool result = perform_dap_transfer(target_dp, transfer_requests, requests, response_data, responses);
if (result || target_dp->fault != DAP_TRANSFER_NO_RESPONSE)
return result;
DEBUG_WARN("Recovering and re-trying access\n");
target_dp->error(target_dp, true);
return perform_dap_transfer(target_dp, transfer_requests, requests, response_data, responses);
}
bool perform_dap_transfer_block_read(
adiv5_debug_port_s *const target_dp, const uint8_t reg, const uint16_t block_count, uint32_t *const blocks)
{
if (block_count > 256U)
return false;
DEBUG_PROBE("-> dap_transfer_block (%u transfer blocks)\n", block_count);
dap_transfer_block_request_read_s request = {
DAP_TRANSFER_BLOCK,
target_dp->dev_index,
{0},
reg | DAP_TRANSFER_RnW,
};
write_le2(request.block_count, 0, block_count);
dap_transfer_block_response_read_s response;
if (!dap_run_cmd(&request, sizeof(request), &response, 3U + (block_count * 4U)))
return false;
const uint16_t blocks_read = read_le2(response.count, 0);
if (blocks_read == block_count && response.status == DAP_TRANSFER_OK) {
for (size_t i = 0; i < block_count; ++i)
blocks[i] = read_le4(response.data[i], 0);
return true;
}
if (response.status != DAP_TRANSFER_OK)
target_dp->fault = response.status;
else
target_dp->fault = 0;
DEBUG_PROBE("-> transfer failed with %u after processing %u blocks\n", response.status, blocks_read);
return false;
}
bool perform_dap_transfer_block_write(
adiv5_debug_port_s *const target_dp, const uint8_t reg, const uint16_t block_count, const uint32_t *const blocks)
{
if (block_count > 256U)
return false;
DEBUG_PROBE("-> dap_transfer_block (%u transfer blocks)\n", block_count);
dap_transfer_block_request_write_s request = {
DAP_TRANSFER_BLOCK,
target_dp->dev_index,
{0},
reg & ~DAP_TRANSFER_RnW,
};
write_le2(request.block_count, 0, block_count);
for (size_t i = 0; i < block_count; ++i)
write_le4(request.data[i], 0, blocks[i]);
dap_transfer_block_response_write_s response;
if (!dap_run_cmd(&request, DAP_CMD_BLOCK_WRITE_HDR_LEN + (block_count * 4U), &response, sizeof(response)))
return false;
const uint16_t blocks_written = read_le2(response.count, 0);
if (blocks_written == block_count && response.status == DAP_TRANSFER_OK)
return true;
if (response.status != DAP_TRANSFER_OK)
target_dp->fault = response.status;
else
target_dp->fault = 0;
DEBUG_PROBE("-> transfer failed with %u after processing %u blocks\n", response.status, blocks_written);
return false;
}
bool perform_dap_swj_sequence(size_t clock_cycles, const uint8_t *data)
{
if (clock_cycles > 256)
return false;
DEBUG_PROBE("-> dap_swj_sequence (%zu cycles)\n", clock_cycles);
uint8_t request[34] = {
DAP_SWJ_SEQUENCE,
(uint8_t)clock_cycles,
};
const size_t bytes = (clock_cycles + 7U) >> 3U;
memcpy(request + 2U, data, bytes);
uint8_t response = DAP_RESPONSE_OK;
if (!dap_run_cmd(request, 2U + bytes, &response, 1U))
return false;
return response == DAP_RESPONSE_OK;
}
bool perform_dap_jtag_sequence(
const uint8_t *const data_in, uint8_t *const data_out, const bool final_tms, const size_t clock_cycles)
{
if (clock_cycles > 64)
return false;
DEBUG_PROBE("-> dap_jtag_sequence (%zu cycles)\n", clock_cycles);
if (!clock_cycles)
return true;
const uint8_t capture_tdo = data_out ? DAP_JTAG_TDO_CAPTURE : 0U;
const uint8_t sequences = final_tms && clock_cycles > 1 ? 2U : 1U;
const uint8_t cycles = clock_cycles - (sequences - 1U);
uint8_t request[14] = {
DAP_JTAG_SEQUENCE,
sequences,
(cycles & 63U) | (final_tms && sequences == 1U ? DAP_JTAG_TMS_SET : DAP_JTAG_TMS_CLEAR) | capture_tdo,
};
const size_t sequence_length = (cycles + 7U) >> 3U;
memcpy(request + 3U, data_in, sequence_length);
size_t offset = 3U + sequence_length;
const uint8_t final_byte = cycles >> 3U;
const uint8_t final_bit = cycles & 7U;
if (sequences == 2U) {
request[offset++] = 1U | DAP_JTAG_TMS_SET | DAP_JTAG_TDO_CAPTURE | capture_tdo;
request[offset++] = data_in[final_byte] >> final_bit;
}
const size_t response_length = capture_tdo ? sequence_length + (sequences == 2U ? 1U : 0U) : 0U;
uint8_t response[6U] = {DAP_RESPONSE_OK};
if (!dap_run_cmd(request, offset, response, 1U + response_length)) {
DEBUG_PROBE("-> sequence failed with %u\n", response[0U]);
return false;
}
if (response_length) {
memcpy(data_out, response + 1U, sequence_length);
if (sequences == 2U)
data_out[final_byte] = (response[1 + sequence_length] & 1U) << final_bit;
}
return response[0] == DAP_RESPONSE_OK;
}
bool perform_dap_jtag_tms_sequence(const uint64_t tms_states, const size_t clock_cycles)
{
if (clock_cycles > 64)
return false;
DEBUG_PROBE("-> dap_jtag_sequence (%zu cycles)\n", clock_cycles);
if (!clock_cycles)
return true;
uint8_t request[130] = {
DAP_JTAG_SEQUENCE,
clock_cycles,
};
size_t offset = 2;
for (size_t cycle = 0; cycle < clock_cycles; ++cycle) {
const bool tms = (tms_states >> cycle) & 1U;
request[offset + 0] = 1U | (tms ? DAP_JTAG_TMS_SET : DAP_JTAG_TMS_CLEAR);
request[offset + 1] = 1U;
offset += 2U;
}
uint8_t response = DAP_RESPONSE_OK;
if (!dap_run_cmd(request, offset, &response, 1U)) {
DEBUG_PROBE("-> sequence failed with %u\n", response);
return false;
}
return response == DAP_RESPONSE_OK;
}
static size_t dap_encode_swd_sequence(
const dap_swd_sequence_s *const sequence, uint8_t *const buffer, const size_t offset)
{
if (sequence->cycles > 64U)
return 0U;
const uint8_t clock_cycles = sequence->cycles;
buffer[offset] = (clock_cycles & 0x3fU) | (sequence->direction << 7U);
if (sequence->direction == DAP_SWD_OUT_SEQUENCE) {
const size_t bytes = (clock_cycles + 7U) >> 3U;
memcpy(buffer + offset + 1U, sequence->data, bytes);
return 1U + bytes;
}
return 1U;
}
bool perform_dap_swd_sequences(dap_swd_sequence_s *const sequences, const uint8_t sequence_count)
{
if (sequence_count > 4)
return false;
DEBUG_PROBE("-> dap_swd_sequence (%u sequences)\n", sequence_count);
uint8_t request[38] = {
DAP_SWD_SEQUENCE,
sequence_count,
};
size_t offset = 2U;
size_t result_length = 0U;
for (uint8_t i = 0; i < sequence_count; ++i) {
const dap_swd_sequence_s *const sequence = &sequences[i];
const size_t adjustment = dap_encode_swd_sequence(sequence, request, offset);
if (!adjustment)
return false;
offset += adjustment;
if (sequence->direction == DAP_SWD_IN_SEQUENCE)
result_length += (sequence->cycles + 7U) >> 3U;
}
uint8_t response[33] = {DAP_RESPONSE_OK};
if (!dap_run_cmd(request, offset, response, 1U + result_length)) {
DEBUG_PROBE("-> sequence failed with %u\n", response[0]);
return false;
}
offset = 1U;
for (uint8_t i = 0; i < sequence_count; ++i) {
dap_swd_sequence_s *const sequence = &sequences[i];
if (sequence->direction == DAP_SWD_OUT_SEQUENCE)
continue;
const size_t bytes = (sequence->cycles + 7U) >> 3U;
memcpy(sequence->data, response + offset, bytes);
offset += bytes;
}
return response[0] == DAP_RESPONSE_OK;
}