Add IPDBG JtagHost functionality to OpenOCD 38/5938/10
authorDaniel Anselmi <danselmi@gmx.ch>
Sun, 11 Oct 2020 13:13:05 +0000 (15:13 +0200)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 8 May 2021 08:51:04 +0000 (09:51 +0100)
IPDBG are utilities to debug IP-cores. It uses JTAG for
transport to/from the FPGA. The different UIs use TCP/IP
as transport. The JtagHost makes the bridge between these
two.

Comparable to the bridge between GDB and the in-circuit-
debugging-unit of a micro controller.

Change-Id: Ib1bc10dcbd4ea426e492bb7b2d85c1ed1b7a8d5a
Signed-off-by: Daniel Anselmi <danselmi@gmx.ch>
Reviewed-on: http://openocd.zylin.com/5938
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
doc/openocd.texi
src/helper/command.h
src/jtag/core.c
src/server/Makefile.am
src/server/ipdbg.c [new file with mode: 0644]
src/server/ipdbg.h [new file with mode: 0644]

index a8a413c4f461ffc66926d0677bc839832e51038c..6614f4832fb5e57f91c4ab123fb5f27591170ede 100644 (file)
@@ -10585,6 +10585,49 @@ If @emph{xsvfdump} shows a file is using those opcodes, it
 probably will not be usable with other XSVF tools.
 
 
+@section IPDBG: JTAG-Host server
+@cindex IPDBG JTAG-Host server
+@cindex IPDBG
+
+IPDBG is a set of tools to debug IP-Cores. It comprises, among others, a logic analyzer and an arbitrary
+waveform generator. These are synthesize-able hardware descriptions of
+logic circuits in addition to software for control, visualization and further analysis.
+In a session using JTAG for its transport protocol, OpenOCD supports the function
+of a JTAG-Host. The JTAG-Host is needed to connect the circuit over JTAG to the
+control-software. For more details see @url{http://ipdbg.org}.
+
+@deffn {Command} {ipdbg} [@option{-start|-stop}] @option{-tap @var{tapname}} @option{-hub @var{ir_value} [@var{dr_length}]} [@option{-port @var{number}}] [@option{-tool @var{number}}] [@option{-vir [@var{vir_value} [@var{length} [@var{instr_code}]]]}]
+Starts or stops a IPDBG JTAG-Host server. Arguments can be specified in any order.
+
+Command options:
+@itemize @bullet
+@item @option{-start|-stop} starts or stops a IPDBG JTAG-Host server (default: start).
+@item @option{-tap @var{tapname}} targeting the TAP @var{tapname}.
+@item @option{-hub @var{ir_value}} states that the JTAG hub is
+reachable with dr-scans while the JTAG instruction register has the value @var{ir_value}.
+@item @option{-port @var{number}} tcp port number where the JTAG-Host is listening.
+@item @option{-tool @var{number}} number of the tool/feature. These corresponds to the ports "data_(up/down)_(0..6)" at the JtagHub.
+@item @option{-vir [@var{vir_value} [@var{length} [@var{instr_code}]]]} On some devices, the user data-register is only reachable if there is a
+specific value in a second dr. This second dr is called vir (virtual ir). With this parameter given, the IPDBG satisfies this condition prior an
+access to the IPDBG-Hub. The value shifted into the vir is given by the first parameter @var{vir_value} (default: 0x11). The second
+parameter @var{length} is the length of the vir data register (default: 5). With the @var{instr_code} (default: 0x00e) parameter the ir value to
+shift data through vir can be configured.
+@end itemize
+@end deffn
+
+Examples:
+@example
+ipdbg -start -tap xc6s.tap -hub 0x02 -port 4242 -tool 4
+@end example
+Starts a server listening on tcp-port 4242 which connects to tool 4.
+The connection is through the TAP of a Xilinx Spartan 6 on USER1 instruction (tested with a papillion pro board).
+
+@example
+ipdbg -start -tap 10m50.tap -hub 0x00C -vir -port 60000 -tool 1
+@end example
+Starts a server listening on tcp-port 60000 which connects to tool 1 (data_up_1/data_down_1).
+The connection is through the TAP of a Intel MAX10 virtual jtag component (sld_instance_index is 0; sld_ir_width is smaller than 5).
+
 @node Utility Commands
 @chapter Utility Commands
 @cindex Utility Commands
index 68f4c14fe116ff405374dce390929f51b966c40c..068df9d0e3d4ff2006f18b2a8b82ff9617f899a5 100644 (file)
@@ -426,6 +426,48 @@ DECLARE_PARSE_WRAPPER(_target_addr, target_addr_t);
 #define COMMAND_PARSE_ADDRESS(in, out) \
        COMMAND_PARSE_NUMBER(target_addr, in, out)
 
+/**
+ * @brief parses the command argument at position @a argn into @a out
+ * as a @a type, or prints a command error referring to @a name_str
+ * and passes the error code to the caller. @a argn will be incremented
+ * if no error occurred. Otherwise the calling function will return
+ * the error code produced by the parsing function.
+ *
+ * This function may cause the calling function to return immediately,
+ * so it should be used carefully to avoid leaking resources.  In most
+ * situations, parsing should be completed in full before proceeding
+ * to allocate resources, and this strategy will most prevents leaks.
+ */
+#define COMMAND_PARSE_ADDITIONAL_NUMBER(type, argn, out, name_str) \
+       do { \
+               if (argn+1 >= CMD_ARGC || CMD_ARGV[argn+1][0] == '-') { \
+                       command_print(CMD, "no " name_str " given"); \
+                       return ERROR_FAIL; \
+               } \
+               ++argn; \
+               COMMAND_PARSE_NUMBER(type, CMD_ARGV[argn], out); \
+       } while (0)
+
+/**
+ * @brief parses the command argument at position @a argn into @a out
+ * as a @a type if the argument @a argn does not start with '-'.
+ * and passes the error code to the caller. @a argn will be incremented
+ * if no error occurred. Otherwise the calling function will return
+ * the error code produced by the parsing function.
+ *
+ * This function may cause the calling function to return immediately,
+ * so it should be used carefully to avoid leaking resources.  In most
+ * situations, parsing should be completed in full before proceeding
+ * to allocate resources, and this strategy will most prevents leaks.
+ */
+#define COMMAND_PARSE_OPTIONAL_NUMBER(type, argn, out) \
+       do { \
+               if (argn+1 < CMD_ARGC && CMD_ARGV[argn+1][0] != '-') { \
+                       ++argn; \
+                       COMMAND_PARSE_NUMBER(type, CMD_ARGV[argn], out); \
+               } \
+       } while (0)
+
 /**
  * Parse the string @c as a binary parameter, storing the boolean value
  * in @c out.  The strings @c on and @c off are used to match different
index 37924aad03e27f917018006a05845073d720ac0a..147df28549be301b7779658d0063cfb12c86ab68 100644 (file)
@@ -45,6 +45,9 @@
 #include "svf/svf.h"
 #include "xsvf/xsvf.h"
 
+/* ipdbg are utilities to debug IP-cores. It uses JTAG for transport. */
+#include "server/ipdbg.h"
+
 /** The number of JTAG queue flushes (for profiling and debugging purposes). */
 static int jtag_flush_queue_count;
 
@@ -1975,7 +1978,12 @@ static int jtag_select(struct command_context *ctx)
        if (retval != ERROR_OK)
                return retval;
 
-       return xsvf_register_commands(ctx);
+       retval = xsvf_register_commands(ctx);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ipdbg_register_commands(ctx);
 }
 
 static struct transport jtag_transport = {
index d270ee2811f4cd5a19c0d7d919629d75ec8b4f3c..5f7469a8495db91f793037aa88f2e3046ea5fd7d 100644 (file)
@@ -10,7 +10,9 @@ noinst_LTLIBRARIES += %D%/libserver.la
        %D%/tcl_server.c \
        %D%/tcl_server.h \
        %D%/rtt_server.c \
-       %D%/rtt_server.h
+       %D%/rtt_server.h \
+       %D%/ipdbg.c \
+       %D%/ipdbg.h
 
 %C%_libserver_la_CFLAGS = $(AM_CFLAGS)
 if IS_MINGW
diff --git a/src/server/ipdbg.c b/src/server/ipdbg.c
new file mode 100644 (file)
index 0000000..ec2fae8
--- /dev/null
@@ -0,0 +1,782 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2020 by Daniel Anselmi <danselmi@gmx.ch> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/bits.h>
+#include <helper/time_support.h>
+#include <jtag/jtag.h>
+#include <server/server.h>
+#include <target/target.h>
+
+#include "ipdbg.h"
+
+#define IPDBG_BUFFER_SIZE 16384
+#define IPDBG_MIN_NUM_OF_OPTIONS 4
+#define IPDBG_MAX_NUM_OF_OPTIONS 14
+#define IPDBG_MIN_DR_LENGTH 11
+#define IPDBG_MAX_DR_LENGTH 13
+#define IPDBG_TCP_PORT_STR_MAX_LENGTH 6
+
+/* private connection data for IPDBG */
+struct ipdbg_fifo {
+       size_t count;
+       size_t rd_idx;
+       char buffer[IPDBG_BUFFER_SIZE];
+};
+
+struct ipdbg_connection {
+       struct ipdbg_fifo dn_fifo;
+       struct ipdbg_fifo up_fifo;
+       bool closed;
+};
+
+struct ipdbg_service {
+       struct ipdbg_hub *hub;
+       struct ipdbg_service *next;
+       uint16_t port;
+       struct ipdbg_connection connection;
+       uint8_t tool;
+};
+
+struct ipdbg_virtual_ir_info {
+       uint32_t instruction;
+       uint32_t length;
+       uint32_t value;
+};
+
+struct ipdbg_hub {
+       uint32_t user_instruction;
+       uint32_t max_tools;
+       uint32_t active_connections;
+       uint32_t active_services;
+       uint32_t valid_mask;
+       uint32_t xoff_mask;
+       uint32_t tool_mask;
+       uint32_t last_dn_tool;
+       struct ipdbg_hub *next;
+       struct jtag_tap *tap;
+       struct connection **connections;
+       uint8_t data_register_length;
+       uint8_t dn_xoff;
+       struct ipdbg_virtual_ir_info *virtual_ir;
+};
+
+static struct ipdbg_hub *ipdbg_first_hub;
+
+static struct ipdbg_service *ipdbg_first_service;
+
+static void ipdbg_init_fifo(struct ipdbg_fifo *fifo)
+{
+       fifo->count = 0;
+       fifo->rd_idx = 0;
+}
+
+static bool ipdbg_fifo_is_empty(struct ipdbg_fifo *fifo)
+{
+       return fifo->count == 0;
+}
+
+static bool ipdbg_fifo_is_full(struct ipdbg_fifo *fifo)
+{
+       return fifo->count == IPDBG_BUFFER_SIZE;
+}
+
+static void ipdbg_zero_rd_idx(struct ipdbg_fifo *fifo)
+{
+       if (fifo->rd_idx == 0)
+               return;
+
+       size_t ri = fifo->rd_idx;
+       for (size_t idx = 0 ; idx < fifo->count ; ++idx)
+               fifo->buffer[idx] = fifo->buffer[ri++];
+       fifo->rd_idx = 0;
+}
+
+static void ipdbg_append_to_fifo(struct ipdbg_fifo *fifo, char data)
+{
+       if (ipdbg_fifo_is_full(fifo))
+               return;
+
+       ipdbg_zero_rd_idx(fifo);
+       fifo->buffer[fifo->count++] = data;
+}
+
+static char ipdbg_get_from_fifo(struct ipdbg_fifo *fifo)
+{
+       if (ipdbg_fifo_is_empty(fifo))
+               return 0;
+
+       fifo->count--;
+       return fifo->buffer[fifo->rd_idx++];
+}
+
+static int ipdbg_move_buffer_to_connection(struct connection *conn, struct ipdbg_fifo *fifo)
+{
+       if (ipdbg_fifo_is_empty(fifo))
+               return ERROR_OK;
+
+       struct ipdbg_connection *connection = conn->priv;
+       if (connection->closed)
+               return ERROR_SERVER_REMOTE_CLOSED;
+
+       ipdbg_zero_rd_idx(fifo);
+       size_t bytes_written = connection_write(conn, fifo->buffer, fifo->count);
+       if (bytes_written != fifo->count) {
+               LOG_ERROR("error during write: %zu != %zu", bytes_written, fifo->count);
+               connection->closed = true;
+               return ERROR_SERVER_REMOTE_CLOSED;
+       }
+
+       fifo->count -= bytes_written;
+
+       return ERROR_OK;
+}
+
+static int ipdbg_max_tools_from_data_register_length(uint8_t data_register_length)
+{
+       int max_tools = 1;
+       data_register_length -= 10; /* 8 bit payload, 1 xoff-flag, 1 valid-flag; remaining bits used to select tool*/
+       while (data_register_length--)
+               max_tools *= 2;
+
+       /* last tool is used to reset JtagCDC and transfer "XON" to host*/
+       return max_tools - 1;
+}
+
+static struct ipdbg_service *ipdbg_find_service(struct ipdbg_hub *hub, uint8_t tool)
+{
+       struct ipdbg_service *service;
+       for (service = ipdbg_first_service ; service ; service = service->next) {
+               if (service->hub == hub && service->tool == tool)
+                       break;
+       }
+       return service;
+}
+
+static void ipdbg_add_service(struct ipdbg_service *service)
+{
+       struct ipdbg_service *iservice;
+       if (ipdbg_first_service) {
+               for (iservice = ipdbg_first_service ; iservice->next; iservice = iservice->next)
+                       ;
+               iservice->next = service;
+       } else
+               ipdbg_first_service = service;
+}
+
+static int ipdbg_create_service(struct ipdbg_hub *hub, uint8_t tool, struct ipdbg_service **service, uint16_t port)
+{
+       *service = calloc(1, sizeof(struct ipdbg_service));
+       if (!*service) {
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+
+       (*service)->hub = hub;
+       (*service)->tool = tool;
+       (*service)->port = port;
+
+       return ERROR_OK;
+}
+
+static int ipdbg_remove_service(struct ipdbg_service *service)
+{
+       if (!ipdbg_first_service)
+               return ERROR_FAIL;
+
+       if (service == ipdbg_first_service) {
+               ipdbg_first_service = ipdbg_first_service->next;
+               return ERROR_OK;
+       }
+
+       for (struct ipdbg_service *iservice = ipdbg_first_service ; iservice->next ; iservice = iservice->next) {
+               if (service == iservice->next) {
+                       iservice->next = service->next;
+                       return ERROR_OK;
+               }
+       }
+       return ERROR_FAIL;
+}
+
+static struct ipdbg_hub *ipdbg_find_hub(struct jtag_tap *tap,
+                               uint32_t user_instruction, struct ipdbg_virtual_ir_info *virtual_ir)
+{
+       struct ipdbg_hub *hub = NULL;
+       for (hub = ipdbg_first_hub ; hub ; hub = hub->next) {
+               if (hub->tap == tap && hub->user_instruction == user_instruction) {
+                       if ((!virtual_ir && !hub->virtual_ir) ||
+                                (virtual_ir && hub->virtual_ir &&
+                                 virtual_ir->instruction == hub->virtual_ir->instruction &&
+                                 virtual_ir->length == hub->virtual_ir->length &&
+                                 virtual_ir->value == hub->virtual_ir->value)) {
+                               break;
+                       }
+               }
+       }
+       return hub;
+}
+
+static void ipdbg_add_hub(struct ipdbg_hub *hub)
+{
+       struct ipdbg_hub *ihub;
+       if (ipdbg_first_hub) {
+               for (ihub = ipdbg_first_hub ; ihub->next; ihub = ihub->next)
+                       ;
+               ihub->next = hub;
+       } else
+               ipdbg_first_hub = hub;
+}
+
+static int ipdbg_create_hub(struct jtag_tap *tap, uint32_t user_instruction, uint8_t data_register_length,
+                                         struct ipdbg_virtual_ir_info *virtual_ir, struct ipdbg_hub **hub)
+{
+       *hub = NULL;
+       struct ipdbg_hub *new_hub = calloc(1, sizeof(struct ipdbg_hub));
+       if (!new_hub) {
+               free(virtual_ir);
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+
+       new_hub->max_tools = ipdbg_max_tools_from_data_register_length(data_register_length);
+       new_hub->connections = calloc(new_hub->max_tools, sizeof(struct connection *));
+       if (!new_hub->connections) {
+               free(virtual_ir);
+               free(new_hub);
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+       new_hub->tap                  = tap;
+       new_hub->user_instruction     = user_instruction;
+       new_hub->data_register_length = data_register_length;
+       new_hub->valid_mask           = BIT(data_register_length - 1);
+       new_hub->xoff_mask            = BIT(data_register_length - 2);
+       new_hub->tool_mask            = (new_hub->xoff_mask - 1) >> 8;
+       new_hub->last_dn_tool         = new_hub->tool_mask;
+       new_hub->virtual_ir           = virtual_ir;
+
+       *hub = new_hub;
+
+       return ERROR_OK;
+}
+
+static void ipdbg_free_hub(struct ipdbg_hub *hub)
+{
+       if (!hub)
+               return;
+       free(hub->connections);
+       free(hub->virtual_ir);
+       free(hub);
+}
+
+static int ipdbg_remove_hub(struct ipdbg_hub *hub)
+{
+       if (!ipdbg_first_hub)
+               return ERROR_FAIL;
+       if (hub == ipdbg_first_hub) {
+               ipdbg_first_hub = ipdbg_first_hub->next;
+               return ERROR_OK;
+       }
+
+       for (struct ipdbg_hub *ihub = ipdbg_first_hub ; ihub->next ; ihub = ihub->next) {
+               if (hub == ihub->next) {
+                       ihub->next = hub->next;
+                       return ERROR_OK;
+               }
+       }
+
+       return ERROR_FAIL;
+}
+
+static void ipdbg_init_scan_field(struct scan_field *fields, uint8_t *in_value, int num_bits, const uint8_t *out_value)
+{
+       fields->check_mask = NULL;
+       fields->check_value = NULL;
+       fields->in_value = in_value;
+       fields->num_bits = num_bits;
+       fields->out_value = out_value;
+}
+
+static int ipdbg_shift_instr(struct ipdbg_hub *hub, uint32_t instr)
+{
+       if (!hub)
+               return ERROR_FAIL;
+
+       struct jtag_tap *tap = hub->tap;
+       if (!tap)
+               return ERROR_FAIL;
+
+       if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) == instr) {
+               /* there is already the requested instruction in the ir */
+               return ERROR_OK;
+       }
+
+       uint8_t *ir_out_val = calloc(DIV_ROUND_UP(tap->ir_length, 8), 1);
+       buf_set_u32(ir_out_val, 0, tap->ir_length, instr);
+
+       struct scan_field fields;
+       ipdbg_init_scan_field(&fields, NULL, tap->ir_length, ir_out_val);
+       jtag_add_ir_scan(tap, &fields, TAP_IDLE);
+       int retval = jtag_execute_queue();
+
+       free(ir_out_val);
+
+       return retval;
+}
+
+static int ipdbg_shift_vir(struct ipdbg_hub *hub)
+{
+       if (!hub)
+               return ERROR_FAIL;
+
+       if (!hub->virtual_ir)
+               return ERROR_OK;
+
+       int retval = ipdbg_shift_instr(hub, hub->virtual_ir->instruction);
+       if (retval != ERROR_OK)
+               return retval;
+
+       struct jtag_tap *tap = hub->tap;
+       if (!tap)
+               return ERROR_FAIL;
+
+       uint8_t *dr_out_val = calloc(DIV_ROUND_UP(hub->virtual_ir->length, 8), 1);
+       buf_set_u32(dr_out_val, 0, hub->virtual_ir->length, hub->virtual_ir->value);
+
+       struct scan_field fields;
+       ipdbg_init_scan_field(&fields, NULL, hub->virtual_ir->length, dr_out_val);
+       jtag_add_dr_scan(tap, 1, &fields, TAP_IDLE);
+       retval = jtag_execute_queue();
+
+       free(dr_out_val);
+
+       return retval;
+}
+
+static int ipdbg_shift_data(struct ipdbg_hub *hub, uint32_t dn_data, uint32_t *up_data)
+{
+       if (!hub)
+               return ERROR_FAIL;
+
+       struct jtag_tap *tap = hub->tap;
+       if (!tap)
+               return ERROR_FAIL;
+
+       uint8_t *dr_out_val = calloc(DIV_ROUND_UP(hub->data_register_length, 8), 1);
+       buf_set_u32(dr_out_val, 0, hub->data_register_length, dn_data);
+       uint8_t *dr_in_val = up_data ? calloc(DIV_ROUND_UP(hub->data_register_length, 8), 1) : NULL;
+
+       struct scan_field fields;
+       ipdbg_init_scan_field(&fields, dr_in_val, hub->data_register_length, dr_out_val);
+       jtag_add_dr_scan(tap, 1, &fields, TAP_IDLE);
+       int retval = jtag_execute_queue();
+
+       if (up_data && retval == ERROR_OK)
+               *up_data = buf_get_u32(dr_in_val, 0, hub->data_register_length);
+
+       free(dr_out_val);
+       free(dr_in_val);
+
+       return retval;
+}
+
+static int ipdbg_distribute_data_from_hub(struct ipdbg_hub *hub, uint32_t up)
+{
+       const bool valid_up_data = up & hub->valid_mask;
+       if (!valid_up_data)
+               return ERROR_OK;
+
+       const size_t tool = (up >> 8) & hub->tool_mask;
+       if (tool == hub->tool_mask) {
+               const uint8_t xon_cmd = up & 0x00ff;
+               hub->dn_xoff &= ~xon_cmd;
+               LOG_INFO("received xon cmd: %d\n", xon_cmd);
+               return ERROR_OK;
+       }
+
+       struct connection *conn = hub->connections[tool];
+       if (conn) {
+               struct ipdbg_connection *connection = conn->priv;
+               if (ipdbg_fifo_is_full(&connection->up_fifo)) {
+                       int retval = ipdbg_move_buffer_to_connection(conn, &connection->up_fifo);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+               ipdbg_append_to_fifo(&connection->up_fifo, up);
+       }
+       return ERROR_OK;
+}
+
+static int ipdbg_jtag_transfer_byte(struct ipdbg_hub *hub, size_t tool, struct ipdbg_connection *connection)
+{
+       uint32_t dn = hub->valid_mask | ((tool & hub->tool_mask) << 8) |
+                               (0x00fful & ipdbg_get_from_fifo(&connection->dn_fifo));
+       uint32_t up = 0;
+       int ret = ipdbg_shift_data(hub, dn, &up);
+       if (ret != ERROR_OK)
+               return ret;
+
+       ret = ipdbg_distribute_data_from_hub(hub, up);
+       if (ret != ERROR_OK)
+               return ret;
+
+       if ((up & hub->xoff_mask) && (hub->last_dn_tool != hub->max_tools)) {
+               hub->dn_xoff |= BIT(hub->last_dn_tool);
+               LOG_INFO("tool %d sent xoff", hub->last_dn_tool);
+       }
+
+       hub->last_dn_tool = tool;
+
+       return ERROR_OK;
+}
+
+static int ipdbg_polling_callback(void *priv)
+{
+       struct ipdbg_hub *hub = priv;
+
+       int ret = ipdbg_shift_vir(hub);
+       if (ret != ERROR_OK)
+               return ret;
+
+       ret = ipdbg_shift_instr(hub, hub->user_instruction);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* transfer dn buffers to jtag-hub */
+       unsigned int num_transfers = 0;
+       for (size_t tool = 0 ; tool < hub->max_tools ; ++tool) {
+               struct connection *conn = hub->connections[tool];
+               if (conn && conn->priv) {
+                       struct ipdbg_connection *connection = conn->priv;
+                       while (((hub->dn_xoff & BIT(tool)) == 0) && !ipdbg_fifo_is_empty(&connection->dn_fifo)) {
+                               ret = ipdbg_jtag_transfer_byte(hub, tool, connection);
+                               if (ret != ERROR_OK)
+                                       return ret;
+                               ++num_transfers;
+                       }
+               }
+       }
+
+       /* some transfers to get data from jtag-hub in case there is no dn data */
+       while (num_transfers++ < hub->max_tools) {
+               uint32_t dn = 0;
+               uint32_t up = 0;
+
+               int retval = ipdbg_shift_data(hub, dn, &up);
+               if (retval != ERROR_OK)
+                       return ret;
+
+               retval = ipdbg_distribute_data_from_hub(hub, up);
+               if (retval != ERROR_OK)
+                       return ret;
+       }
+
+       /* write from up fifos to sockets */
+       for (size_t tool = 0 ; tool < hub->max_tools ; ++tool) {
+               struct connection *conn = hub->connections[tool];
+               if (conn && conn->priv) {
+                       struct ipdbg_connection *connection = conn->priv;
+                       int retval = ipdbg_move_buffer_to_connection(conn, &connection->up_fifo);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int ipdbg_start_polling(struct ipdbg_service *service, struct connection *connection)
+{
+       struct ipdbg_hub *hub = service->hub;
+       hub->connections[service->tool] = connection;
+       hub->active_connections++;
+       if (hub->active_connections > 1) {
+               /* hub is already initialized */
+               return ERROR_OK;
+       }
+
+       const uint32_t reset_hub = hub->valid_mask | ((hub->max_tools) << 8);
+
+       int ret = ipdbg_shift_vir(hub);
+       if (ret != ERROR_OK)
+               return ret;
+
+       ret = ipdbg_shift_instr(hub, hub->user_instruction);
+       if (ret != ERROR_OK)
+               return ret;
+
+       ret = ipdbg_shift_data(hub, reset_hub, NULL);
+       hub->last_dn_tool = hub->tool_mask;
+       hub->dn_xoff = 0;
+       if (ret != ERROR_OK)
+               return ret;
+
+       LOG_INFO("IPDBG start_polling");
+
+       const int time_ms = 20;
+       const int periodic = 1;
+       return target_register_timer_callback(ipdbg_polling_callback, time_ms, periodic, hub);
+}
+
+static int ipdbg_stop_polling(struct ipdbg_service *service)
+{
+       struct ipdbg_hub *hub = service->hub;
+       hub->connections[service->tool] = NULL;
+       hub->active_connections--;
+       if (hub->active_connections == 0) {
+               LOG_INFO("IPDBG stop_polling");
+
+               return target_unregister_timer_callback(ipdbg_polling_callback, hub);
+       }
+
+       return ERROR_OK;
+}
+
+static int ipdbg_on_new_connection(struct connection *connection)
+{
+       struct ipdbg_service *service = connection->service->priv;
+       connection->priv = &service->connection;
+       /* initialize ipdbg connection information */
+       ipdbg_init_fifo(&service->connection.up_fifo);
+       ipdbg_init_fifo(&service->connection.dn_fifo);
+
+       int retval = ipdbg_start_polling(service, connection);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("BUG: ipdbg_start_polling failed");
+               return retval;
+       }
+
+       struct ipdbg_connection *conn = connection->priv;
+       conn->closed = false;
+
+       LOG_INFO("New IPDBG Connection");
+
+       return ERROR_OK;
+}
+
+static int ipdbg_on_connection_input(struct connection *connection)
+{
+       struct ipdbg_connection *conn = connection->priv;
+       struct ipdbg_fifo *fifo = &conn->dn_fifo;
+
+       if (ipdbg_fifo_is_full(fifo))
+               return ERROR_OK;
+
+       ipdbg_zero_rd_idx(fifo);
+       int bytes_read = connection_read(connection, fifo->buffer + fifo->count, IPDBG_BUFFER_SIZE - fifo->count);
+       if (bytes_read <= 0) {
+               if (bytes_read < 0)
+                       LOG_ERROR("error during read: %s", strerror(errno));
+               return ERROR_SERVER_REMOTE_CLOSED;
+       }
+
+       fifo->count += bytes_read;
+
+       return ERROR_OK;
+}
+
+static int ipdbg_on_connection_closed(struct connection *connection)
+{
+       struct ipdbg_connection *conn = connection->priv;
+       conn->closed = true;
+       LOG_INFO("Closed IPDBG Connection");
+
+       return ipdbg_stop_polling(connection->service->priv);
+}
+
+static int ipdbg_start(uint16_t port, struct jtag_tap *tap, uint32_t user_instruction,
+                                       uint8_t data_register_length, struct ipdbg_virtual_ir_info *virtual_ir, uint8_t tool)
+{
+       LOG_INFO("starting ipdbg service on port %d for tool %d", port, tool);
+
+       struct ipdbg_hub *hub = ipdbg_find_hub(tap, user_instruction, virtual_ir);
+       if (hub) {
+               free(virtual_ir);
+               if (hub->data_register_length != data_register_length) {
+                       LOG_DEBUG("hub must have the same data_register_length for all tools");
+                       return ERROR_FAIL;
+               }
+       } else {
+               int retval = ipdbg_create_hub(tap, user_instruction, data_register_length, virtual_ir, &hub);
+               if (retval != ERROR_OK) {
+                       free(virtual_ir);
+                       return retval;
+               }
+       }
+
+       struct ipdbg_service *service = NULL;
+       int retval = ipdbg_create_service(hub, tool, &service, port);
+
+       if (retval != ERROR_OK || !service) {
+               if (hub->active_services == 0 && hub->active_connections == 0)
+                       ipdbg_free_hub(hub);
+               return ERROR_FAIL;
+       }
+
+       char port_str_buffer[IPDBG_TCP_PORT_STR_MAX_LENGTH];
+       snprintf(port_str_buffer, IPDBG_TCP_PORT_STR_MAX_LENGTH, "%u", port);
+       retval = add_service("ipdbg", port_str_buffer, 1, &ipdbg_on_new_connection,
+               &ipdbg_on_connection_input, &ipdbg_on_connection_closed, service);
+       if (retval == ERROR_OK) {
+               ipdbg_add_service(service);
+               if (hub->active_services == 0 && hub->active_connections == 0)
+                       ipdbg_add_hub(hub);
+               hub->active_services++;
+       } else {
+               if (hub->active_services == 0 && hub->active_connections == 0)
+                       ipdbg_free_hub(hub);
+               free(service);
+       }
+
+       return retval;
+}
+
+static int ipdbg_stop(struct jtag_tap *tap, uint32_t user_instruction,
+                       struct ipdbg_virtual_ir_info *virtual_ir, uint8_t tool)
+{
+       struct ipdbg_hub *hub = ipdbg_find_hub(tap, user_instruction, virtual_ir);
+       free(virtual_ir);
+       if (!hub)
+               return ERROR_FAIL;
+
+       struct ipdbg_service *service = ipdbg_find_service(hub, tool);
+       if (!service)
+               return ERROR_FAIL;
+
+       int retval = ipdbg_remove_service(service);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("BUG: ipdbg_remove_service failed");
+               return retval;
+       }
+
+       char port_str_buffer[IPDBG_TCP_PORT_STR_MAX_LENGTH];
+       snprintf(port_str_buffer, IPDBG_TCP_PORT_STR_MAX_LENGTH, "%u", service->port);
+       retval = remove_service("ipdbg", port_str_buffer);
+       /* The ipdbg_service structure is freed by server.c:remove_service().
+          There the "priv" pointer is freed.*/
+       if (retval != ERROR_OK) {
+               LOG_ERROR("BUG: remove_service failed");
+               return retval;
+       }
+       hub->active_services--;
+       if (hub->active_connections == 0 && hub->active_services == 0) {
+               retval = ipdbg_remove_hub(hub);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("BUG: ipdbg_remove_hub failed");
+                       return retval;
+               }
+               ipdbg_free_hub(hub);
+       }
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_ipdbg_command)
+{
+       struct jtag_tap *tap = NULL;
+       uint16_t port = 4242;
+       uint8_t tool = 1;
+       uint32_t user_instruction = 0x00;
+       uint8_t data_register_length = IPDBG_MAX_DR_LENGTH;
+       bool start = true;
+       bool hub_configured = false;
+       bool has_virtual_ir = false;
+       uint32_t virtual_ir_instruction = 0x00e;
+       uint32_t virtual_ir_length = 5;
+       uint32_t virtual_ir_value = 0x11;
+       struct ipdbg_virtual_ir_info *virtual_ir = NULL;
+
+       if ((CMD_ARGC < IPDBG_MIN_NUM_OF_OPTIONS) || (CMD_ARGC > IPDBG_MAX_NUM_OF_OPTIONS))
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       for (unsigned int i = 0; i < CMD_ARGC; ++i) {
+               if (strcmp(CMD_ARGV[i], "-tap") == 0) {
+                       if (i + 1 >= CMD_ARGC || CMD_ARGV[i + 1][0] == '-') {
+                               command_print(CMD, "no TAP given");
+                               return ERROR_FAIL;
+                       }
+                       tap = jtag_tap_by_string(CMD_ARGV[i + 1]);
+                       if (!tap) {
+                               command_print(CMD, "Tap %s unknown", CMD_ARGV[i + 1]);
+                               return ERROR_FAIL;
+                       }
+                       ++i;
+               } else if (strcmp(CMD_ARGV[i], "-hub") == 0) {
+                       COMMAND_PARSE_ADDITIONAL_NUMBER(u32, i, user_instruction, "ir_value to select hub");
+                       hub_configured = true;
+                       COMMAND_PARSE_OPTIONAL_NUMBER(u8, i, data_register_length);
+                       if (data_register_length < IPDBG_MIN_DR_LENGTH ||
+                               data_register_length > IPDBG_MAX_DR_LENGTH) {
+                               command_print(CMD, "length of \"user\"-data register must be at least %d and at most %d.",
+                                                       IPDBG_MIN_DR_LENGTH, IPDBG_MAX_DR_LENGTH);
+                               return ERROR_FAIL;
+                       }
+               } else if (strcmp(CMD_ARGV[i], "-vir") == 0) {
+                       COMMAND_PARSE_OPTIONAL_NUMBER(u32, i, virtual_ir_value);
+                       COMMAND_PARSE_OPTIONAL_NUMBER(u32, i, virtual_ir_length);
+                       COMMAND_PARSE_OPTIONAL_NUMBER(u32, i, virtual_ir_instruction);
+                       has_virtual_ir = true;
+               } else if (strcmp(CMD_ARGV[i], "-port") == 0) {
+                       COMMAND_PARSE_ADDITIONAL_NUMBER(u16, i, port, "port number");
+               } else if (strcmp(CMD_ARGV[i], "-tool") == 0) {
+                       COMMAND_PARSE_ADDITIONAL_NUMBER(u8, i, tool, "tool");
+               } else if (strcmp(CMD_ARGV[i], "-stop") == 0) {
+                       start = false;
+               } else if (strcmp(CMD_ARGV[i], "-start") == 0) {
+                       start = true;
+               } else {
+                       command_print(CMD, "Unknown argument: %s", CMD_ARGV[i]);
+                       return ERROR_FAIL;
+               }
+       }
+
+       if (!tap) {
+               command_print(CMD, "no valid tap selected");
+               return ERROR_FAIL;
+       }
+
+       if (!hub_configured) {
+               command_print(CMD, "hub not configured correctly");
+               return ERROR_FAIL;
+       }
+
+       if (tool >= ipdbg_max_tools_from_data_register_length(data_register_length)) {
+               command_print(CMD, "Tool: %d is invalid", tool);
+               return ERROR_FAIL;
+       }
+
+       if (has_virtual_ir) {
+               virtual_ir = calloc(1, sizeof(struct ipdbg_virtual_ir_info));
+               if (!virtual_ir) {
+                       LOG_ERROR("Out of memory");
+                       return ERROR_FAIL;
+               }
+               virtual_ir->instruction = virtual_ir_instruction;
+               virtual_ir->length      = virtual_ir_length;
+               virtual_ir->value       = virtual_ir_value;
+       }
+
+       if (start)
+               return ipdbg_start(port, tap, user_instruction, data_register_length, virtual_ir, tool);
+       else
+               return ipdbg_stop(tap, user_instruction, virtual_ir, tool);
+}
+
+static const struct command_registration ipdbg_command_handlers[] = {
+       {
+               .name = "ipdbg",
+               .handler = handle_ipdbg_command,
+               .mode = COMMAND_EXEC,
+               .help = "Starts or stops an IPDBG JTAG-Host server.",
+               .usage = "[-start|-stop] -tap device.tap -hub ir_value [dr_length]"
+                                " [-port number] [-tool number] [-vir [vir_value [length [instr_code]]]]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+int ipdbg_register_commands(struct command_context *cmd_ctx)
+{
+       return register_commands(cmd_ctx, NULL, ipdbg_command_handlers);
+}
diff --git a/src/server/ipdbg.h b/src/server/ipdbg.h
new file mode 100644 (file)
index 0000000..6b70545
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2020 by Daniel Anselmi <danselmi@gmx.ch> */
+
+#ifndef OPENOCD_IPDBG_IPDBG_H
+#define OPENOCD_IPDBG_IPDBG_H
+
+#include <helper/command.h>
+
+int ipdbg_register_commands(struct command_context *cmd_ctx);
+
+#endif /* OPENOCD_IPDBG_IPDBG_H */

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)