blob: 2319f36d2a2203430db4c6582b2979bdb8ee704e [file] [log] [blame]
/* Target dependent code for ARC processor family, for GDB, the GNU debugger.
Copyright 2009 Free Software Foundation, Inc.
Contributed by ARC International (www.arc.com)
Authors:
Richard Stuckey <richard.stuckey@arc.com>
This file is part of GDB.
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/>. */
/******************************************************************************/
/* */
/* Outline: */
/* This module creates an instance of a gdb 'target_ops' structure which */
/* contains information and operations for debugging using the ARC xISS */
/* (Fast Instruction Set Simulator) as the target. */
/* */
/* Instruction Tracing: */
/* Two different kind of instruction tracing are supported: */
/* */
/* 1) a minimal trace which records only the address of each instruction */
/* which is executed; */
/* */
/* 2) a more detailed trace which includes the ordinal number of the */
/* instruction in the trace, condition code settings, instruction */
/* address, instruction disassembly, and the values of source and */
/* destination operands. */
/* */
/* The minimal trace is recorded by the xISS in an internal buffer whose */
/* size may be determined by the gdb user; this buffer wraps around, so */
/* that only the addresses of the most recently executed instructions are */
/* held. This data may then be saved to a file for subsequent inspection. */
/* The trace data is written in an encoding which gives a reduction in */
/* size of approximately 80% compared to the raw data. */
/* */
/* See the manual */
/* */
/* ARC GDB-Insight */
/* GNU Debugger With Insight GUI */
/* Getting Started */
/* 6009-001 */
/* */
/* for full information on how to use these trace facilities. */
/* */
/* Usage: */
/* The module exports a function _initialize_arc_xiss: the call to this */
/* function is generated by the gdb build mechanism, so this function */
/* should not be explicitly called. */
/* */
/******************************************************************************/
/* gdb configuration file */
#include "config.h"
/* This macro should be defined in the configuration file only if the arcint.h
file is available. */
#ifdef HAVE_LIBXISS
/* system header files */
#include <unistd.h>
#include <errno.h>
#include <dlfcn.h>
/* gdb header files */
#include "defs.h"
#include "inferior.h"
#include "disasm.h"
#include "gdbcmd.h"
#include "objfiles.h"
#include "libiberty.h"
#include "completer.h"
#include "gdb_assert.h"
/* ARC header files */
#include "config/arc/tm-embed.h"
#include "arc-xiss.h"
#include "arc-tdep.h"
#include "arc-elf32-tdep.h"
#include "arc-architecture.h"
#include "arc-inst-tracing.h"
#include "arc-registers.h"
/* This must be defined before arcint.h is included. */
#define OEM_USE_OF_DEBUGGER_HEADER_FILES 1
/* This defines the interface by which the xISS is accessed. */
#include "arcint.h"
/* -------------------------------------------------------------------------- */
/* local types */
/* -------------------------------------------------------------------------- */
typedef struct ARC* (*ARC_Interface)(void);
typedef enum
{
XISS_HALTED,
XISS_STEPPED,
XISS_TO_BE_RUN
} ExecutionStatus;
/* N.B. this is taken from arcint.cpp; there must be a better way of doing this! */
struct ARC
{
ARC_functab *pftp; /* Pointer to interface function table. */
};
typedef void* WatchpointCookie;
typedef struct _association
{
WatchpointCookie cookie;
CORE_ADDR addr;
int length;
int type;
} Association;
/* -------------------------------------------------------------------------- */
/* local data */
/* -------------------------------------------------------------------------- */
#define ARC_TARGET_NAME "arcxiss"
#define ARC_INTERFACE "get_ARC_interface"
#define SAVE_TRACE_TO_FILE_COMMAND "arc-xiss-save-trace"
#define EMPTY_TRACE_BUFFER_COMMAND "arc-xiss-empty-trace-buffer"
#define LIST_TRACE_COMMAND "arc-xiss-list-trace"
#define SAVE_TRACE_TO_FILE_COMMAND_USAGE "Usage: " SAVE_TRACE_TO_FILE_COMMAND " <FILE>\n"
#define EMPTY_TRACE_BUFFER_COMMAND_USAGE "Usage: " EMPTY_TRACE_BUFFER_COMMAND "\n"
#define LIST_TRACE_COMMAND_USAGE "Usage: " LIST_TRACE_COMMAND " [ FROM=<VALUE> ] [ TO=<VALUE> ] [ <FILE> ]\n"
/* N.B. the xISS does not currently use the context parameter supplied for
memory read/write operations, so its value is irrelevant. */
#define XISS_CONTEXT 0
/* The gdb target operations structure for this target. */
static struct target_ops xISS_target_ops;
/* A set of pointers to operations for reading/writing registers/memory in the
xISS target. */
static TargetOperations operations;
/* Handles for access to the xISS target. */
static void *xISS_DLL_handle;
static struct ARC *xiss_instance;
static ARC_functab *xISS_functions;
/* The status of program execution on the xISS target. */
static ExecutionStatus xISS_executionStatus;
/* Data for handling instruction tracing. */
static Boolean xISS_trace_instructions;
static char *xiss_trace_file;
static int xiss_trace_buffer_size;
/* This is the table for associating watchpoint details with watchpoint cookies. */
static Association *associations;
static unsigned int num_associations;
/* The address of data to which access caused a watchpoint to trigger. */
static CORE_ADDR stopped_data_address;
/* -------------------------------------------------------------------------- */
/* local macros */
/* -------------------------------------------------------------------------- */
#define EXTRACT(argument, type, result) \
{ \
struct expression *expr = parse_expression(argument); \
struct value *val = evaluate_expression(expr); \
struct cleanup *chain = make_cleanup(free_current_contents, &expr); \
\
result = *(type*) (value_contents (val)); \
result ## _specified = TRUE; \
do_cleanups (chain); \
}
/* This macro must cope with the cases:
var = value
var =value
var= value
var=value */
#define GET_PARAMETER(var) \
if (strncasecmp(argv[i], #var "=", sizeof(#var)) == 0) \
{ \
if (argv[i][sizeof(#var)] == '\0') \
{ \
i++; \
if (argv[i] == NULL) \
invalid = TRUE; \
else \
{ \
EXTRACT(argv[i], Ordinal, var) \
i++; \
} \
} \
else \
{ \
EXTRACT(argv[i] + sizeof(#var), Ordinal, var) \
i++; \
} \
} \
else if (strcasecmp(argv[i], #var) == 0) \
{ \
i++; \
if (argv[i] == NULL) \
invalid = TRUE; \
else \
{ \
if (argv[i][0] == '=') \
{ \
if (argv[i][1] == '\0') \
{ \
i++; \
if (argv[i] == NULL) \
invalid = TRUE; \
else \
{ \
EXTRACT(argv[i], Ordinal, var) \
i++; \
} \
} \
else \
{ \
EXTRACT(argv[i] + 1, Ordinal, var) \
i++; \
} \
} \
else \
invalid = TRUE; \
} \
}
#define CHECK_RANGE(source) \
{ \
if (from_specified) \
{ \
if (from < first || from > last) \
{ \
warning(_("%s contains instruction range %llu .. %llu - using %llu as FROM"), \
source, first, last, first); \
gdb_flush(gdb_stderr); \
from = first; \
} \
} \
else \
from = first; \
\
if (to_specified) \
{ \
if (to < first || to > last) \
{ \
warning(_("%s contains instruction range %llu .. %llu - using %llu as TO"), \
source, first, last, last); \
gdb_flush(gdb_stderr); \
to = last; \
} \
} \
else \
to = last; \
}
#define XISS_PROPERTY(key,value) xISS_functions->process_property(xiss_instance, key, value)
#define IS_SUPPORTED(flag) ((xISS_functions->supports_feature(xiss_instance) & (flag)) != 0)
#define XISS_AUX_REG(hw_regno) ((ARC_REG_TYPE) hw_regno + (ARC_REG_TYPE) AUX_BASE)
#define XISS_CORE_REG(hw_regno) ((ARC_REG_TYPE) hw_regno)
/* -------------------------------------------------------------------------- */
/* local functions */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* 1) functions for watchpoint table */
/* -------------------------------------------------------------------------- */
/* These functions implement a linear lookup table which associate an xISS
watchpoint cookie with the watchpoint's address, length and type; this is
not very efficient, but is quite sufficient for relatively small numbers of
watchpoints. If we should need to handle large numbers of watchpoints
efficiently, a more sophisticated data structure may be required. */
/* Add an association to the table. */
static void
associate (WatchpointCookie cookie, CORE_ADDR addr, int length, int type)
{
Association *new = NULL;
unsigned int i;
for (i = 0; i < num_associations; i++)
{
Association *association = &associations[i];
if (association->cookie == NULL)
{
new = association;
break;
}
}
if (new == NULL)
{
Association *new_associations = xrealloc(associations, sizeof(Association) * (num_associations + 1));
if (new_associations == NULL)
nomem(0);
associations = new_associations;
new = &new_associations[num_associations];
num_associations++;
}
new->cookie = cookie;
new->addr = addr;
new->length = length;
new->type = type;
}
/* Remove an association from the table, returning the cookie. */
static WatchpointCookie
disassociate (CORE_ADDR addr, int length, int type)
{
unsigned int i;
for (i = 0; i < num_associations; i++)
{
Association *association = &associations[i];
if (association->addr == addr &&
association->length == length &&
association->type == type)
{
WatchpointCookie cookie = association->cookie;
association->cookie = NULL;
return cookie;
}
}
return NULL;
}
/* Find the address associated with a cookie. */
static CORE_ADDR
find (WatchpointCookie cookie)
{
unsigned int i;
for (i = 0; i < num_associations; i++)
{
Association *association = &associations[i];
if (association->cookie == cookie)
return association->addr;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* 2) functions for reading/writing registers */
/* -------------------------------------------------------------------------- */
/* N.B. the register contents returned by these functions, or supplied to them,
are in host byte order - the arcint.cpp interface to the xISS requires
this. */
/* Read a core register on the target.
Parameters:
hw_regno : the ARC hardware number of the register
value : set to the contents of the register
warn_on_failure: TRUE if a warning should be issued if the read fails
Result: TRUE if the register contents are read. */
static Boolean
read_xiss_core_register (ARC_RegisterNumber hw_regno,
ARC_RegisterContents *contents,
Boolean warn_on_failure)
{
unsigned long value;
int result = xISS_functions->read_reg(xiss_instance, XISS_CORE_REG(hw_regno), &value);
if (result == 1)
{
*contents = (ARC_RegisterContents) value;
DEBUG("Read value 0x%08X from core register %d\n", *contents, hw_regno);
return TRUE;
}
if (warn_on_failure)
arc_elf32_core_warning(ERROR_ON_READING_REGISTER, hw_regno);
return FALSE;
}
/* Write a core register on the target.
Parameters:
hw_regno : the ARC hardware number of the register
value : set to the contents of the register
warn_on_failure: TRUE if a warning should be issued if the write fails
Result: TRUE if the register contents are written. */
static Boolean
write_xiss_core_register (ARC_RegisterNumber hw_regno,
ARC_RegisterContents contents,
Boolean warn_on_failure)
{
int result = xISS_functions->write_reg(xiss_instance, XISS_CORE_REG(hw_regno), (unsigned long) contents);
if (result == 1)
{
DEBUG("Written value 0x%08X to core register %d\n", contents, hw_regno);
return TRUE;
}
if (warn_on_failure)
arc_elf32_core_warning(ERROR_ON_WRITING_REGISTER, hw_regno);
return FALSE;
}
/* Read an auxiliary register on the target.
Parameters:
hw_regno : the ARC hardware number of the register
value : set to the contents of the register
warn_on_failure: TRUE if a warning should be issued if the read fails
Result: TRUE if the register contents are read. */
static Boolean
read_xiss_aux_register (ARC_RegisterNumber hw_regno,
ARC_RegisterContents *contents,
Boolean warn_on_failure)
{
unsigned long value;
int result = xISS_functions->read_reg(xiss_instance, XISS_AUX_REG(hw_regno), &value);
if (result == 1)
{
*contents = (ARC_RegisterContents) value;
DEBUG("Read value 0x%08X from auxiliary register %d\n", *contents, hw_regno);
return TRUE;
}
if (warn_on_failure)
arc_elf32_aux_warning(ERROR_ON_READING_REGISTER, hw_regno);
return FALSE;
}
/* Write an auxiliary register on the target.
Parameters:
hw_regno : the ARC hardware number of the register
value : the contents of the register
warn_on_failure: TRUE if a warning should be issued if the write fails
Result: TRUE if the register contents are written. */
static Boolean
write_xiss_aux_register (ARC_RegisterNumber hw_regno,
ARC_RegisterContents contents,
Boolean warn_on_failure)
{
ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno);
int result;
if (def)
contents = arc_write_value(def, contents);
result = xISS_functions->write_reg(xiss_instance, XISS_AUX_REG(hw_regno), (unsigned long) contents);
if (result == 1)
{
DEBUG("Written value 0x%08X to auxiliary register %d\n", contents, hw_regno);
return TRUE;
}
if (warn_on_failure)
arc_elf32_aux_warning(ERROR_ON_WRITING_REGISTER, hw_regno);
return FALSE;
}
/* -------------------------------------------------------------------------- */
/* 3) functions for reading/writing memory */
/* -------------------------------------------------------------------------- */
/* These functions should NOT be used within this module: they are intended
purely for use by the arc-memory module for reading/writing multiple words
of data at word-aligned addresses. */
static unsigned int
read_words (ARC_Address address,
ARC_Byte *data,
unsigned int words)
{
DEBUG("reading %u words from 0x%08X in xISS\n", words, address);
gdb_assert(IS_WORD_ALIGNED(address));
return (unsigned int) xISS_functions->read_memory
(xiss_instance,
(unsigned long) address,
data,
(unsigned long) words * BYTES_IN_WORD,
XISS_CONTEXT);
}
static unsigned int
write_words (ARC_Address address,
ARC_Byte *data,
unsigned int words)
{
gdb_assert(IS_WORD_ALIGNED(address));
DEBUG("writing %u words to 0x%08X in xISS\n", words, address);
return (unsigned int) xISS_functions->write_memory
(xiss_instance,
(unsigned long) address,
data,
(unsigned long) words * BYTES_IN_WORD,
XISS_CONTEXT);
}
static unsigned int
write_pattern (ARC_Address address,
ARC_Word pattern,
unsigned int words)
{
gdb_assert(IS_WORD_ALIGNED(address));
DEBUG("writing pattern 0x%08X repeated %u times to 0x%08X in xISS\n", pattern, words, address);
return (unsigned int) xISS_functions->fill_memory
(xiss_instance,
(unsigned long) address,
&pattern,
BYTES_IN_WORD,
(unsigned int) words,
XISS_CONTEXT);
}
/* -------------------------------------------------------------------------- */
/* 4) functions for xISS interface management */
/* -------------------------------------------------------------------------- */
/* Open the interface to the debug target.
Parameters:
systemfile: the .xis file defining the simulated target
from_tty : non-zero if the 'target' command was issued at the terminal
*/
static Boolean
open_xISS_interface (const char *systemfile, int from_tty)
{
const char *xiss_home = getenv("XISS_HOME");
char library[FILENAME_MAX];
ARC_Interface arc_interface;
if (xiss_home == NULL)
error(_("Environment variable XISS_HOME is not set"));
/* Construct the path to the xISS .so file. */
(void) snprintf(library, sizeof(library), "%s/lib/libxiss.so", xiss_home);
/* Try to dynamically load the shared object library containing the xISS. */
xISS_DLL_handle = dlopen(library, RTLD_LAZY);
if (xISS_DLL_handle == NULL)
{
const char *diagnostic = dlerror();
if (strstr(diagnostic, library))
error(_("Can not open xISS shared library %s"), diagnostic);
else
error(_("Can not open xISS shared library %s: %s"), library, diagnostic);
}
/* Find the function in the library which will create an instacne of the xISS. */
arc_interface = dlsym(xISS_DLL_handle, ARC_INTERFACE);
if (arc_interface == NULL)
error(_("Can not find function %s in xISS shared library %s"), ARC_INTERFACE, library);
/* Create an xISS instance. */
xiss_instance = arc_interface();
if (xiss_instance == NULL)
error(_("Can not create instance of xISS"));
/* Get a pointer to the table of functions provided by the interface. */
xISS_functions = xiss_instance->pftp;
DEBUG("xISS interface : %s\n", xISS_functions->id(xiss_instance));
DEBUG("xISS interface version: %d\n", xISS_functions->version(xiss_instance));
/* Tell the xISS what system file to use to define the simulated target. */
if (XISS_PROPERTY("xiss_sys", systemfile) != 1)
error(_("xISS could not process 'xiss_sys' property"));
if (IS_SUPPORTED(ARC_FEATURE_fill_memory))
operations.fill_memory = write_pattern;
(void) xISS_functions->prepare_for_new_program(xiss_instance, 1);
/* This is somewhat inelegant, but commands read from scripts in the gdb
testsuite are regarded as though they were being input interactively
(i.e. from_tty is 1), and interactive queries may be made (such as
asking the user whether the program currently being debugged should be
killed first) - and these queries hang the tests!
So, if the environment variable is set, assume that the gdb test suite is
being run, so that no such queries will be made.
It is not possible to make this check in the top-level command handler
loop, as the output from some other commands (e.g. 'file') depend on the
from_tty parameter passed to them, and the gdb test scripts expect to get
the interactive version of the output! */
target_preopen(from_tty && (getenv("ARC_GDB_TEST") == NULL));
return TRUE;
}
/* Close the JTAG interface to the debug target.
Parameter:
resume: TRUE if program execution on the target should be allowed to resume. */
static void
close_xISS_interface (Boolean resume)
{
/* If we have a target connected. */
if (xiss_instance != NULL)
{
/* It is meaningless to resume execution of the xISS. */
arc_elf32_close(FALSE);
/* If we are doing instruction tracing. */
if (xiss_trace_file)
{
/* Ensure that the trace file is closed. */
if (XISS_PROPERTY("trace_file", "") != 1)
error(_("xISS could not process 'trace_file' property"));
}
/* Close the connection. */
xISS_functions->destroy(xiss_instance);
/* Close the library. */
if (dlclose(xISS_DLL_handle) != 0)
warning(_("error on closing xISS shared library: %s"), dlerror());
xiss_instance = NULL;
xISS_functions = NULL;
xISS_DLL_handle = NULL;
}
}
/* -------------------------------------------------------------------------- */
/* 5) functions for starting/stopping the processor */
/* -------------------------------------------------------------------------- */
/* Run the xISS for whatever quantum of instructions that it executes. */
static void
run_processor (void)
{
int result = xISS_functions->run(xiss_instance);
if (result == 0)
warning(_("could not run"));
}
/* -------------------------------------------------------------------------- */
/* 6) local functions called from outside this module (from gdb) */
/* -------------------------------------------------------------------------- */
/* Connect to the xISS target.
Parameters:
args : user arguments to the 'target' command
from_tty: non-zero if the 'target' command was issued at the terminal
The arguments may be: <XIS_system_file>
If the name of a system file is not specified, a file 'default.xis' is
assumed. */
static void
arc_xISS_open (char *args, int from_tty)
{
char *systemfile = (args) ? args : "default.xis";
ENTERARGS("\"%s\" (%d)", (args) ? args : "", from_tty);
if (access(systemfile, R_OK) != 0)
{
char *file = getenv("XISS_SYSTEM_FILE");
if (file == NULL)
error(_("Invalid xISS system file '%s': %s"), systemfile, strerror(errno));
else
if (access(file, R_OK) != 0)
error(_("Invalid xISS system file '%s': %s"), file, strerror(errno));
else
systemfile = file;
}
arc_program_is_loaded = FALSE;
xISS_trace_instructions = FALSE;
xiss_trace_file = NULL;
xISS_executionStatus = XISS_HALTED;
xiss_trace_buffer_size = 0;
num_associations = 0;
associations = NULL;
/* Find any well-known aux register numbers that we will need. */
arc_elf32_find_register_numbers();
/* Just to be sure that it is not in the target stack... */
(void) unpush_target (&xISS_target_ops);
/* Now try to open the xISS interface. */
if (open_xISS_interface(systemfile, from_tty))
{
(void) push_target (&xISS_target_ops);
if (from_tty)
printf_filtered (_("Connected to the " ARC_TARGET_NAME " target.\n"));
}
else
error(_("Can not connect to target"));
}
/* Close the connection to the target. */
static void
arc_xISS_close (int quitting) /* Ignored. */
{
ENTERMSG;
close_xISS_interface(FALSE);
xfree(associations);
associations = NULL;
num_associations = 0;
}
/* Cause the inferior on the debug target to resume execution, sending a signal
if necessary.
Parameters:
ptid : the thread id of the thread to be resumed (ignored)
step : 1 means single step, 0 run freely.
signal: the number of the signal to be sent
N.B. signals are not supported. */
static void
arc_xISS_resume (ptid_t ptid, int step, enum target_signal signal)
{
ENTERARGS("%d, %d, %d", ptid.pid, step, signal);
if (signal != TARGET_SIGNAL_0)
error(_("Signals are not supported by the " ARC_TARGET_NAME " target"));
if (step)
{
int result = xISS_functions->step(xiss_instance);
if (result == 0)
error(_("Can not single-step"));
else
xISS_executionStatus = XISS_STEPPED;
}
else
xISS_executionStatus = XISS_TO_BE_RUN;
LEAVEMSG;
}
/* Wait for execution on the target to halt (for whatever reason).
Parameters :
ptid : ignored
status: set to indicate status at end of the wait
*/
static ptid_t
arc_xISS_wait (ptid_t ptid, struct target_waitstatus *status)
{
ENTERARGS("xISS execution status: %d", xISS_executionStatus);
/* What was done the last time that execution was resumed? */
switch (xISS_executionStatus)
{
case XISS_STEPPED:
DEBUG("processor has stepped\n");
status->kind = TARGET_WAITKIND_STOPPED;
status->value.sig = TARGET_SIGNAL_TRAP;
break;
case XISS_TO_BE_RUN:
arc_elf32_execute(status,
run_processor,
NULL,
NULL);
if (status->kind == TARGET_WAITKIND_EXITED)
target_mark_exited (&xISS_target_ops);
break;
case XISS_HALTED:
break;
}
LEAVEMSG;
return inferior_ptid;
}
/* This gets called just before store_regs. */
static void
arc_xISS_prepare_to_store (struct regcache *regcache)
{
ENTERMSG;
}
static void
arc_xISS_files_info (struct target_ops *target)
{
/* Do nothing. */
ENTERMSG;
}
/* Heavy duty arsenal. Kill the process. */
static void
arc_xISS_kill (void)
{
ENTERMSG;
target_mourn_inferior ();
}
/* Load the program into memory via the xISS interface. */
static void
arc_xISS_load (char *args, int from_tty)
{
/* The program will be downloaded to the simulator. */
(void) xISS_functions->prepare_for_new_program(xiss_instance, 1);
arc_elf32_load_program(args, from_tty);
/* Tell xISS that program is fully loaded. */
if (XISS_PROPERTY("download_complete", "1") != 1)
error(_("xISS could not process 'download_complete' property"));
/* We now have a program ready for execution on the target. */
}
/* Create the inferior that will be executed upon the target.
Parameters :
exec_file: the executable file containing the program to be executed
args : the command line arguments to be passed to the program
env : the environment (name/value pairs) for the program
from_tty : ignored
*/
static void
arc_xISS_create_inferior (char *exec_file, char *args, char **env, int from_tty)
{
arc_elf32_create_inferior(exec_file, args, env, &xISS_target_ops);
}
/* Mourn the inferior. */
static void
arc_xISS_mourn_inferior (void)
{
ENTERMSG;
// (void) unpush_target (&xISS_target_ops);
/* N.B. we must delete all breakpoints from the target here: the gdb core
function generic_mourn_inferior marks all breakpoints as not being
inserted on the target, with the result that subsequent calls to
remove_breakpoints will NOT remove any breakpoints that are set on
the target; this means that if target execution is re-started, gdb
will attempt to re-insert the breakpoints, which causes a problem
with software breakpoints: the target insert_breakpoint function
reads the code at the b/p address (which is the s/w b/p instruction)
and saves it as the "overwritten" code - so when the breakpoint is
subsequently removed, the b/p instruction is written back to the
b/p address again! That is not what is desired... */
(void) remove_breakpoints();
generic_mourn_inferior();
current_target.to_has_execution = 0;
}
/* Check whether the given thread is alive. */
static int
arc_xISS_thread_alive (ptid_t ptid)
{
ENTERMSG;
/* We only have one thread. */
return 1;
}
/* Check whether our debug target is runnable: return 1 if it is, 0 otherwise. */
static int arc_xISS_can_run (void)
{
/* If we are connected to the xISS i/f, and a program is loaded. */
return (xiss_instance != NULL) && arc_program_is_loaded;
}
/* We do not support asynchronous execution of the target program (i.e. commands
like 'run' or 'continue' or 'step' can not be executed in background mode
by appending a '&' to them) so we do not need to implement the target stop
operation (called by the 'interrupt' command); interrupting a running program
is handled by the Ctrl-C mechanism. */
#if 0
static void
arc_xISS_stop (void)
{
ENTERMSG;
}
#endif
/* Check if we can set a "hardware" watchpoint of type TYPE. TYPE is
one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint, or
bp_hardware_breakpoint. COUNT is the number of such watchpoints used so far
(including this one). OTHERTYPE is the total number of "hardware" breakpoints
and watchpoints of other types that are "already" set
(0 if type == bp_hardware_breakpoint).
Result: 0 if hardware watchpoints are not supported
-1 if there are not enough hardware watchpoints
1 if there are enough hardware watchpoints
N.B. this is not what is stated in target.h, but it does conform to the use
made of this function's result in breakpoint.c! */
static int
arc_xISS_can_use_hw_breakpoint (int type, int count, int othertype)
{
ENTERARGS("type %d, count %d", type, count);
switch (type)
{
case bp_hardware_watchpoint:
case bp_read_watchpoint:
case bp_watchpoint: /* This means bp_write_watchpoint. */
return 1;
case bp_hardware_breakpoint:
return 0;
default:
return 0;
}
}
/* Insert a "hardware" watchpoint on the target.
Parameters:
addr : the start address of the region of memory to be watched
length: the length in bytes of the region of memory
type : 0 => write, 1 => read, 2 => read/write
Returns 0 for success, -1 for failure. */
static int
arc_xISS_insert_watchpoint (CORE_ADDR addr, int length, int type)
{
WatchpointCookie cookie;
unsigned int options;
ENTERARGS("0x%08X:%d %d", (unsigned int) addr, length, type);
gdb_assert(length > 0);
switch (type)
{
case 0:
options = ARC_WATCHPOINT_write;
break;
case 1:
options = ARC_WATCHPOINT_read;
break;
case 2:
options = ARC_WATCHPOINT_read | ARC_WATCHPOINT_write;
break;
default:
internal_error (__FILE__, __LINE__, _("invalid watchpoint type: %d"), type);
}
if (xISS_functions->set_mem_watchpoint2(xiss_instance, (unsigned long) addr, length, options, &cookie) == 1)
{
associate(cookie, addr, length, type);
return 0;
}
return 1;
}
/* Remove a "hardware" watchpoint from the target.
Parameters:
addr : the start address of the region of memory being watched
length: the length in bytes of the region of memory
type : 0 => write, 1 => read, 2 => read/write
Returns 0 for success, non-zero for failure. */
static int
arc_xISS_remove_watchpoint (CORE_ADDR addr, int length, int type)
{
WatchpointCookie cookie = disassociate(addr, length, type);
ENTERARGS("0x%x:%d %d", (unsigned int) addr, length, type);
if (cookie != NULL)
return (xISS_functions->remove_watchpoint(xiss_instance, cookie) == 1) ? 0 : 1;
return 1;
}
/* Returns non-zero if the execution of the target program has been stopped by
the trigger of a "hardware" watchpoint (i.e. on memory read or write), zero
otherwise. */
static int
arc_xISS_stopped_by_watchpoint (void)
{
WatchpointCookie cookie;
ENTERMSG;
if (xISS_functions->stopped_at_watchpoint2(xiss_instance, &cookie) == 1)
{
/* Regrettably, the arcint i/f does not provide a well-defined means for
finding out the address of the data which was accessed - to do this
we have had to define a special property which returns the required
address! */
int result = XISS_PROPERTY("watchpoint", "return_hit_address");
if (result == 0)
{
DEBUG("xISS could not process 'watchpoint/return_hit_address' property");
/* The most we can do now is retrieve the start address of the
watchpoint which was triggered...
N.B. we must retrieve the watchpoint address here, rather than in
the arc_xISS_stopped_data_address function, as gdb deletes
all breakpoint and watchpoints from the target as soon as
execution is halted, which removes the cookie from the table! */
stopped_data_address = find(cookie);
}
else
stopped_data_address = (CORE_ADDR) result;
return 1;
}
return 0;
}
/* Get the address of the data that was read/written causing a h/w watchpoint to
trigger; the address is returned in the '*addr' parameter.
Returns 0 for failure, non-zero for success. */
static int
arc_xISS_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr)
{
ENTERMSG;
DEBUG("data addr: 0x%08X\n", (unsigned int) stopped_data_address);
*addr = stopped_data_address;
return 1;
}
/* Can a h/w watchpoint 'length' bytes long be set at address 'addr' in target memory? */
static int
arc_xISS_region_ok_for_hw_watchpoint (CORE_ADDR addr, int length)
{
/* As far as we know, we can set a h/w watchpoint anywhere. */
return 1;
}
/* -------------------------------------------------------------------------- */
/* 7) functions for instruction tracing */
/* -------------------------------------------------------------------------- */
/* Display the instruction at the given address and ordinal position in the
instruction trace. */
static void
display_instruction (Ordinal ordinal, unsigned int address)
{
/* N.B. must use fprintf_filtered here, rather than fprintf_unfiltered, as
gdb_print_insn calls fprintf_filtered, and if we mix the use of the
two we can get garbled output! */
fprintf_filtered(gdb_stdout, "<0x%016llX> 0x%08X ", ordinal, address);
(void) gdb_print_insn((CORE_ADDR) address, gdb_stdout, NULL);
fprintf_filtered(gdb_stdout, "\n");
}
/* Get the ordinal position of the first instruction in the trace buffer in the
sequence of executed instructions from the xISS; return 0 if this number cannot
be obtained. */
static Ordinal
get_instruction_trace_start (void)
{
/* N.B. XISS_PROPERTY returns 0 if the property is not handled. */
int start_low = XISS_PROPERTY("instruction_trace", "return_start_count_low");
int start_high = XISS_PROPERTY("instruction_trace", "return_start_count_high");
Ordinal start = ((Ordinal) start_high) << 32 | (Ordinal) start_low;
DEBUG("get_instruction_trace_start : %llu\n", start);
return start;
}
/* Encode the instruction trace buffer retrieved from the xISS: the buffer
'trace' contains 'length' entries which are all PC values. The data is
recorded in the currently open trace file in a packed format. */
static void
encode_instruction_trace (unsigned int trace[], int length)
{
unsigned int lastPC = trace[0];
int i;
/* Store the first entry in the buffer as an absolute address. */
arc_encode_PC(ABSOLUTE_31_BITS, lastPC / 2);
/* Look at all the remaining entries, comparing each with the previous one. */
for (i = 1; i < length; i++)
{
unsigned int thisPC = trace[i];
int delta = (int) thisPC - (int) lastPC;
/* Encode the difference in the PC as a relative offset, if possible.
Offsets of 0 (branch to current address), 2, 4, 6 and 8 (instruction
sizes + optional immediate size) are handled as special cases. */
if (delta == 0)
arc_encode_PC(NO_CHANGE, 0);
else if (delta == 2)
arc_encode_PC(PLUS_16_BITS, 0);
else if (delta == 4)
arc_encode_PC(PLUS_32_BITS, 0);
else if (delta == 6)
arc_encode_PC(PLUS_48_BITS, 0);
else if (delta == 8)
arc_encode_PC(PLUS_64_BITS, 0);
else if (0 < delta && delta <= MAX_DELTA)
arc_encode_PC(DELTA_16_BIT_POSITIVE, ENCODE_DELTA(delta));
else if (0 > delta && delta >= -MAX_DELTA)
arc_encode_PC(DELTA_16_BIT_NEGATIVE, ENCODE_DELTA(-delta));
else
arc_encode_PC(ABSOLUTE_31_BITS, thisPC / 2);
lastPC = thisPC;
}
/* Useful tracing code: dump buffer as raw binary. */
#if 0
{
int binary = open("trace.binary", O_CREAT | O_WRONLY, 0666);
if (binary == -1)
warning(_("could not open file trace.binary"));
else
{
(void) write(binary, trace, length * sizeof(unsigned int));
(void) close(binary);
}
}
#endif
}
/* Decode the instruction trace buffer retrieved from a file. This is the inverse
of the encoding performed by the 'encode_instruction_trace' function.
The instruction at each PC value decoded is disassembled and displayed only if
its ordinal position in the trace is in the range given by the 'from' and 'to'
parameters. */
static void
decode_instruction_trace (Ordinal from, Ordinal to, Ordinal ordinal)
{
ARC_ProgramCounterEncoding encoding;
unsigned int thisPC = 0;
unsigned int lastPC = 0;
unsigned int value;
while (arc_decode_PC(&encoding, &value))
{
switch (encoding)
{
case NO_CHANGE:
break;
case PLUS_16_BITS:
thisPC += 2;
break;
case PLUS_32_BITS:
thisPC += 4;
break;
case PLUS_48_BITS:
thisPC += 6;
break;
case PLUS_64_BITS:
thisPC += 8;
break;
case DELTA_16_BIT_POSITIVE:
thisPC += DECODE_DELTA(value);
break;
case DELTA_16_BIT_NEGATIVE:
thisPC -= DECODE_DELTA(value);
break;
case ABSOLUTE_31_BITS:
thisPC = value * 2;
break;
}
if (from <= ordinal && ordinal <= to)
display_instruction(ordinal, thisPC);
lastPC = thisPC;
ordinal++;
}
}
/* -------------------------------------------------------------------------- */
/* 8) helper routines for added commands */
/* -------------------------------------------------------------------------- */
/* Check that the xISS target is actually connected. */
static void
check_connected (void)
{
if (xiss_instance == NULL)
error(_("Target " ARC_TARGET_NAME " is not connected"));
}
/* Set the size of the buffer to be used by the xISS for instruction trace data. */
static void
set_trace_buffer_size (int size)
{
char sz[20];
(void) snprintf(sz, sizeof(sz), "%d", size);
if (XISS_PROPERTY("trace_buffer_size", sz) != 1)
error(_("xISS could not process 'trace_buffer_size' property"));
}
/* Tell the xISS to switch instruction tracing on or off. */
static void
set_xiss_trace (char *args,
int from_tty,
struct cmd_list_element *e)
{
check_connected();
if (XISS_PROPERTY("trace", (xISS_trace_instructions) ? "on" : "off") != 1)
error(_("xISS could not process 'trace' property"));
}
/* Show the status of xISS instruction tracing. */
static void
show_xiss_trace (struct ui_file *file,
int from_tty,
struct cmd_list_element *c,
const char *value)
{
/* Value will be either "on" or "off". */
fprintf_filtered(file,
_("xISS instruction tracing is %s.\n"),
value);
}
/* Tell the xISS to write the instruction trace to the file whose name is held
in the xiss_trace_file global variable. */
static void
set_xiss_trace_file (char *args,
int from_tty,
struct cmd_list_element *e)
{
check_connected();
if (xiss_trace_file)
{
if (XISS_PROPERTY("trace_file", xiss_trace_file) != 1)
error(_("xISS could not process 'trace_file' property"));
}
}
/* Show the name of the trace file (if any) which is currently receiving
instruction tracing output from the xISS. */
static void
show_xiss_trace_file (struct ui_file *file,
int from_tty,
struct cmd_list_element *c,
const char *value)
{
if (*value == '\0')
fprintf_filtered(file,
_("No output file is set for xISS instruction tracing.\n"));
else
fprintf_filtered(file,
_("The output file for xISS instruction tracing is '%s'.\n"),
value);
}
/* Set the size of the buffer used by the xISS for instruction trace data. */
static void
set_xiss_trace_buffer_size (char *args,
int from_tty,
struct cmd_list_element *e)
{
check_connected();
if (xiss_trace_buffer_size < 0)
error(_("Trace buffer size must be non-negative"));
set_trace_buffer_size(xiss_trace_buffer_size);
}
/* Show the size of the buffer used by the xISS for instruction trace data. */
static void
show_xiss_trace_buffer_size (struct ui_file *file,
int from_tty,
struct cmd_list_element *c,
const char *value)
{
fprintf_filtered(file,
_("The buffer size for xISS instruction tracing is %s entries.\n"),
value);
}
/* -------------------------------------------------------------------------- */
/* 9) local functions implementing commands */
/* -------------------------------------------------------------------------- */
/* Command: <command> <filename>
Save the contents of the xISS instruction trace buffer to the named file.
We should eventually change this to use the ui_out stuff rather than
printf_filtered. */
static void
arc_save_trace_to_file_command (char *arg, int from_tty)
{
int count;
if (!arg)
{
printf_filtered (_(SAVE_TRACE_TO_FILE_COMMAND_USAGE));
return;
}
check_connected();
count = xISS_functions->instruction_trace_count(xiss_instance);
if (count)
{
unsigned int *buffer;
if (access(arg, F_OK) == 0)
if (!query(_("File already exists. Do you wish to overwrite it?")))
return;
printf_unfiltered(_("%u instructions in trace buffer\n"), count);
buffer = xmalloc(count * sizeof(unsigned int));
if (buffer)
{
Ordinal first = get_instruction_trace_start();
xISS_functions->get_instruction_traces(xiss_instance, buffer);
if (arc_start_encoding(arg, first))
{
encode_instruction_trace(buffer, count);
arc_stop_encoding(first + (Ordinal) count - 1);
}
xfree(buffer);
}
else
warning(_("can not allocate buffer to hold instruction trace data"));
}
else
warning(_("no instruction trace data available"));
}
/* Command: <command>
Discard the contents of the xISS instruction trace buffer. */
static void
arc_empty_trace_buffer (char *arg, int from_tty)
{
if (arg)
{
printf_filtered (_(EMPTY_TRACE_BUFFER_COMMAND_USAGE));
return;
}
check_connected();
set_trace_buffer_size(0);
set_trace_buffer_size(xiss_trace_buffer_size);
}
/* Command: <command> [ FROM=<from> ] [ TO=<to> ] [ <FILE> ]
Display some or all of the instruction trace, either from the xISS trace
buffer or from a named file. */
static void
arc_list_trace (char *arg, int from_tty)
{
char *file = NULL;
Boolean from_specified = FALSE;
Boolean to_specified = FALSE;
Ordinal from = 0;
Ordinal to = 0;
Ordinal first;
Ordinal last;
/* Do we have arguments to the command? */
if (arg)
{
char **argv = buildargv (arg);
Boolean invalid = FALSE;
int i = 0;
if (argv == NULL)
nomem (0);
while (argv[i] != NULL)
{
// printf("argv[%d] = %s\n", i, argv[i]);
GET_PARAMETER(from)
else
GET_PARAMETER(to)
else
{
/* Assume the argument is the file name. */
file = xstrdup(argv[i]);
i++;
}
}
freeargv(argv);
if (invalid)
{
printf_filtered (_(LIST_TRACE_COMMAND_USAGE));
return;
}
DEBUG("FROM = %llu\n", from);
DEBUG("TO = %llu\n", to);
if (from > to)
error("FROM (%lld) > TO (%lld)", from, to);
}
/* If we must get the instruction trace from a file. */
if (file)
{
/* Try to open the named file and start decoding its contents. */
if (arc_start_decoding(file, &first, &last))
{
CHECK_RANGE("file")
decode_instruction_trace(from, to, first);
arc_stop_decoding();
}
xfree(file);
}
else
{
unsigned int count;
check_connected();
/* Get the number of entries in the xISS instruction trace buffer. */
count = (unsigned int) xISS_functions->instruction_trace_count(xiss_instance);
if (count > 0)
{
unsigned int *buffer = xmalloc(count * sizeof(unsigned int));
if (buffer)
{
Ordinal i;
first = get_instruction_trace_start();
last = first + (Ordinal) count - 1;
CHECK_RANGE("trace buffer")
/* Get the contents of the xISS instruction trace buffer. */
xISS_functions->get_instruction_traces(xiss_instance, buffer);
/* Display the required range of the trace. */
for (i = from; i <= to; i++)
display_instruction(i, buffer[(int) (i - from)]);
xfree(buffer);
}
else
warning(_("can not allocate buffer to hold instruction trace data"));
}
else
warning(_("no instruction trace data available"));
}
}
/* -------------------------------------------------------------------------- */
/* 10) initialization functions */
/* -------------------------------------------------------------------------- */
/* Initialize the xISS target operations. */
static void
initialize_xISS_target_ops (void)
{
ENTERMSG;
xISS_target_ops.to_data = &operations;
xISS_target_ops.to_shortname = ARC_TARGET_NAME;
xISS_target_ops.to_longname = "xISS debug target (ARC Processors)";
xISS_target_ops.to_doc = "xISS (Fast Instruction Set Simulator) debug target (ARC Processors)";
xISS_target_ops.to_open = arc_xISS_open;
xISS_target_ops.to_close = arc_xISS_close;
xISS_target_ops.to_resume = arc_xISS_resume;
xISS_target_ops.to_wait = arc_xISS_wait;
xISS_target_ops.to_fetch_registers = arc_elf32_fetch_registers;
xISS_target_ops.to_store_registers = arc_elf32_store_registers;
xISS_target_ops.to_prepare_to_store = arc_xISS_prepare_to_store;
xISS_target_ops.to_xfer_partial = arc_elf32_xfer_partial;
xISS_target_ops.to_files_info = arc_xISS_files_info;
xISS_target_ops.to_can_use_hw_breakpoint = arc_xISS_can_use_hw_breakpoint;
// xISS_target_ops.to_insert_hw_breakpoint = arc_xISS_insert_hw_breakpoint;
// xISS_target_ops.to_remove_hw_breakpoint = arc_xISS_remove_hw_breakpoint;
xISS_target_ops.to_insert_watchpoint = arc_xISS_insert_watchpoint;
xISS_target_ops.to_remove_watchpoint = arc_xISS_remove_watchpoint;
xISS_target_ops.to_stopped_by_watchpoint = arc_xISS_stopped_by_watchpoint;
xISS_target_ops.to_stopped_data_address = arc_xISS_stopped_data_address;
xISS_target_ops.to_region_ok_for_hw_watchpoint = arc_xISS_region_ok_for_hw_watchpoint;
xISS_target_ops.to_insert_breakpoint = arc_elf32_insert_breakpoint;
xISS_target_ops.to_remove_breakpoint = arc_elf32_remove_breakpoint;
xISS_target_ops.to_kill = arc_xISS_kill;
xISS_target_ops.to_load = arc_xISS_load;
xISS_target_ops.to_create_inferior = arc_xISS_create_inferior;
xISS_target_ops.to_mourn_inferior = arc_xISS_mourn_inferior;
xISS_target_ops.to_thread_alive = arc_xISS_thread_alive;
// xISS_target_ops.to_stop = arc_xISS_stop;
xISS_target_ops.to_can_run = arc_xISS_can_run;
xISS_target_ops.to_terminal_inferior = NULL;
xISS_target_ops.to_stratum = process_stratum;
xISS_target_ops.to_has_all_memory = 1;
xISS_target_ops.to_has_memory = 1;
xISS_target_ops.to_has_stack = 0; /* Defer setting this until the program has been loaded. */
xISS_target_ops.to_has_registers = 1;
xISS_target_ops.to_has_execution = 0; /* Defer setting this until the program has been started. */
xISS_target_ops.to_magic = OPS_MAGIC;
}
#endif /* HAVE_LIBXISS */
/* -------------------------------------------------------------------------- */
/* externally visible functions */
/* -------------------------------------------------------------------------- */
/* Initialize the module. This function is called from the gdb core on start-up. */
/* N.B. the initialization function must be defined even if the rest of this
module is excluded, as the call to it from the gdb start-up code is
generated by the build mechanism without regard to any conditional
compilation! */
void
_initialize_arc_xiss (void)
{
#ifdef HAVE_LIBXISS
struct cmd_list_element *cmnd;
ENTERMSG;
operations.read_core_register = read_xiss_core_register;
operations.write_core_register = write_xiss_core_register;
operations.read_auxiliary_register = read_xiss_aux_register;
operations.write_auxiliary_register = write_xiss_aux_register;
operations.read_memory = read_words;
operations.write_memory = write_words;
operations.fill_memory = NULL;
initialize_xISS_target_ops ();
add_target (&xISS_target_ops);
/* Register ARC-specific commands with gdb. */
(void) add_setshow_boolean_cmd
("arc-xiss-trace",
class_trace,
&xISS_trace_instructions,
_("Set whether the xISS should trace instructions.\n"),
_("Show whether the xISS should trace instructions.\n"),
NULL,
set_xiss_trace,
show_xiss_trace,
&setlist,
&showlist);
add_setshow_optional_filename_cmd
("arc-xiss-trace-file",
class_trace,
&xiss_trace_file,
_("Set the output file for xISS instruction tracing.\n"),
_("Show the output file for xISS instruction tracing.\n"),
NULL,
set_xiss_trace_file,
show_xiss_trace_file,
&setlist,
&showlist);
(void) add_setshow_zinteger_cmd
("arc-xiss-trace-buffer-size",
class_trace,
&xiss_trace_buffer_size,
_("Set the size of the trace buffer for xISS instruction tracing.\n"),
_("Show the size of the trace buffer for xISS instruction tracing.\n"),
NULL,
set_xiss_trace_buffer_size,
show_xiss_trace_buffer_size,
&setlist,
&showlist);
cmnd = add_cmd
(SAVE_TRACE_TO_FILE_COMMAND,
class_trace,
arc_save_trace_to_file_command,
_("Save the contents of the xISS instruction trace buffer to a file.\n"
SAVE_TRACE_TO_FILE_COMMAND_USAGE
"<FILE> is a file to hold the xISS instruction trace buffer contents.\n"),
&cmdlist);
set_cmd_completer (cmnd, filename_completer);
(void) add_cmd
(EMPTY_TRACE_BUFFER_COMMAND,
class_trace,
arc_empty_trace_buffer,
_("Empty xISS instruction trace buffer.\n"
EMPTY_TRACE_BUFFER_COMMAND_USAGE),
&cmdlist);
cmnd = add_cmd
(LIST_TRACE_COMMAND,
class_trace,
arc_list_trace,
_("Display xISS instruction trace.\n"
LIST_TRACE_COMMAND_USAGE),
&cmdlist);
set_cmd_completer (cmnd, filename_completer);
#endif /* HAVE_LIBXISS */
}
/******************************************************************************/