openocd: add openocd wrapper
Including * Main loop for controlling pandaboard * Modification routines like setting halt conditions, reading or writing Memory, Registers, etc. * Timers The *.hpp file is defined as *.hpp.in, because the absolute path to config file must be set by CMake-Script (Will be introduced in later commit) Change-Id: I648df4916877dae550943bbb9b264b8d662689b7
This commit is contained in:
732
debuggers/openocd/openocd_wrapper.cc
Normal file
732
debuggers/openocd/openocd_wrapper.cc
Normal file
@ -0,0 +1,732 @@
|
||||
#include "openocd_wrapper.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "config.h"
|
||||
|
||||
#include "src/helper/system.h"
|
||||
#include "src/helper/log.h"
|
||||
#include "src/helper/time_support.h"
|
||||
#include "src/helper/command.h"
|
||||
#include "src/helper/configuration.h"
|
||||
#include "src/helper/util.h"
|
||||
#include "src/helper/ioutil.h"
|
||||
|
||||
#include "src/jtag/jtag.h"
|
||||
|
||||
#include "src/target/algorithm.h"
|
||||
#include "src/target/breakpoints.h"
|
||||
#include "src/target/register.h"
|
||||
#include "src/target/target_type.h"
|
||||
#include "src/target/target.h"
|
||||
#include "src/target/cortex_a.h"
|
||||
#include "src/target/arm_adi_v5.h"
|
||||
#include "src/target/armv7a.h"
|
||||
|
||||
#include "jimtcl/jim.h"
|
||||
|
||||
extern struct command_context *setup_command_handler(Jim_Interp *interp);
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sys/time.h>
|
||||
#include "config/VariantConfig.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::hex;
|
||||
using std::dec;
|
||||
|
||||
/*
|
||||
* Flag for finishing main loop execution.
|
||||
*/
|
||||
static bool oocdw_exection_finished = false;
|
||||
|
||||
/*
|
||||
* Initially set target struct pointers for use in
|
||||
* multiple functions.
|
||||
*/
|
||||
static struct target *target_a9;
|
||||
static struct arm *arm_a9;
|
||||
static struct target *target_m3;
|
||||
static struct command_context *cmd_ctx;
|
||||
|
||||
/*
|
||||
* State variable for propagation of a coming horizontal hop.
|
||||
* For correct execution of a horizontal hop, an additional
|
||||
* single-step needs to be executed before resuming target
|
||||
* execution.
|
||||
* Variable is set while setting new halt_conditions on basis of
|
||||
* current instruction and its memory access(es).
|
||||
*/
|
||||
static bool horizontal_step = false;
|
||||
|
||||
/*
|
||||
* As the normal loop execution is
|
||||
* resume - wait for halt - trigger events and set next halt condition(s),
|
||||
* single-stepping is different. It is accomplished by setting this state
|
||||
* variable.
|
||||
*/
|
||||
static bool single_step_requested = false;
|
||||
|
||||
// Timer related structures
|
||||
#define P_MAX_TIMERS 32
|
||||
struct timer{
|
||||
bool inUse; // Timer slot is in-use (currently registered).
|
||||
uint64_t timeToFire; // Time to fire next in microseconds
|
||||
struct timeval time_begin; // Timer value at activation of timer
|
||||
bool active; // 0=inactive, 1=active.
|
||||
p_timer_handler_t funct; // A callback function for when the
|
||||
// timer fires.
|
||||
void *this_ptr; // The this-> pointer for C++ callbacks
|
||||
// has to be stored as well.
|
||||
#define PMaxTimerIDLen 32
|
||||
char id[PMaxTimerIDLen]; // String ID of timer.
|
||||
};
|
||||
|
||||
struct timer timers[P_MAX_TIMERS];
|
||||
|
||||
fail::Logger LOG("OpenOCD", false);
|
||||
|
||||
/** FORWARD DECLARATIONS **/
|
||||
static void update_timers();
|
||||
static struct watchpoint *getHaltingWatchpoint();
|
||||
static void getCurrentMemAccesses(struct halt_condition *accesses);
|
||||
static uint32_t getCurrentPC();
|
||||
|
||||
/*
|
||||
* Main entry and main loop.
|
||||
*
|
||||
* States:
|
||||
* 0) Init
|
||||
* 1) Reset
|
||||
* 2) Wait for navigational commands from experiment
|
||||
* 3) (Execute single-steps if requested)
|
||||
* 3) Resume execution
|
||||
* 4) Wait for halt
|
||||
* 5) Fire event
|
||||
* 6) update timers
|
||||
* 7) goto 2
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* === INITIALIZATION === */
|
||||
|
||||
// Redirect output to logfile
|
||||
FILE *file = fopen("oocd.log", "w");
|
||||
set_log_output(NULL, file);
|
||||
|
||||
/* initialize commandline interface */
|
||||
cmd_ctx = setup_command_handler(NULL);
|
||||
|
||||
if (util_init(cmd_ctx) != ERROR_OK)
|
||||
return 1;//EXIT_FAILURE;
|
||||
|
||||
if (ioutil_init(cmd_ctx) != ERROR_OK)
|
||||
return 1;//EXIT_FAILURE;
|
||||
|
||||
command_context_mode(cmd_ctx, COMMAND_CONFIG);
|
||||
command_set_output_handler(cmd_ctx, configuration_output_handler, NULL);
|
||||
|
||||
/* Start the executable meat that can evolve into thread in future. */
|
||||
//ret = openocd_thread(argc, argv, cmd_ctx);
|
||||
|
||||
if (parse_cmdline_args(cmd_ctx, argc, argv) != ERROR_OK)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
/*if (server_preinit() != ERROR_OK)
|
||||
return EXIT_FAILURE;*/
|
||||
|
||||
// set path to configuration file
|
||||
add_script_search_dir(OOCD_CONF_FILES_PATH);
|
||||
add_config_command("script "OOCD_CONF_FILE_PATH);
|
||||
|
||||
ret = parse_config_file(cmd_ctx);
|
||||
if (ret != ERROR_OK) {
|
||||
LOG << "Error in openocd configuration!\nFor more detailed information refer to oocd.log" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Servers (gdb/telnet) are not being activated
|
||||
/* ret = server_init(cmd_ctx);
|
||||
if (ERROR_OK != ret)
|
||||
return EXIT_FAILURE;*/
|
||||
|
||||
ret = command_run_line(cmd_ctx, (char*)"init");
|
||||
if (ret != ERROR_OK) {
|
||||
LOG << "Error in openocd initialization!\nFor more detailed information refer to oocd.log" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Find target cortex_a9 core 0
|
||||
target_a9 = get_target("omap4460.cpu");
|
||||
if (!target_a9) {
|
||||
LOG << "FATAL ERROR: Target omap4460.cpu not found!" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find target cortex_m3 core 0
|
||||
target_m3 = get_target("omap4460.m30");
|
||||
if (!target_m3) {
|
||||
LOG << "FATAL ERROR: Target omap4460.m30 not found!" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
arm_a9 = target_to_arm(target_a9);
|
||||
jtag_poll_set_enabled (false);
|
||||
|
||||
LOG << "OpenOCD 0.7.0 for Fail* and Pandaboard initialized" << endl;
|
||||
|
||||
for (int i=0; i<P_MAX_TIMERS; i++) {
|
||||
timers[i].inUse = false;
|
||||
}
|
||||
|
||||
/* === INITIALIZATION COMPLETE => MAIN LOOP === */
|
||||
|
||||
/*
|
||||
* Initial reboot
|
||||
*/
|
||||
oocdw_reboot();
|
||||
|
||||
/*
|
||||
* Switch to experiment for navigational instructions
|
||||
*/
|
||||
fail::simulator.startup();
|
||||
|
||||
while (!oocdw_exection_finished) {
|
||||
|
||||
/*
|
||||
* At this point the device should always be in halted state
|
||||
* So single-stepping is done loop-wise, here.
|
||||
* For having functional timers anyways, we need to update
|
||||
* in every loop iteration
|
||||
*/
|
||||
while (single_step_requested) {
|
||||
if (target_step(target_a9, 1, 0, 1)) {
|
||||
LOG << "FATAL ERROR: Single-step could not be executed" << endl;
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Because this is a micro main-loop, we need to update
|
||||
* timers. This loop can be executed several times (e.g.
|
||||
* in tracing mode), so timers would be neglected otherwise.
|
||||
*/
|
||||
update_timers();
|
||||
uint32_t pc = getCurrentPC();
|
||||
|
||||
/*
|
||||
* Reset request flag. Needs to be done before calling
|
||||
* onBreakpoint, because otherwise it would reset a
|
||||
* newly activated request after coroutine switch.
|
||||
*/
|
||||
single_step_requested = false;
|
||||
|
||||
fail::simulator.onBreakpoint(NULL, pc, fail::ANY_ADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
* In the following loop stage it is assumed, that the target is
|
||||
* running, so execution needs to be resumed, if the target is
|
||||
* halted.
|
||||
*/
|
||||
if (target_a9->state == TARGET_HALTED) {
|
||||
LOG << "Resume" << endl;
|
||||
if (target_resume(target_a9, 1, 0, 1, 1)) {
|
||||
LOG << "FATAL ERROR: Target could not be resumed!" << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Polling needs to be done to detect target halt state changes.
|
||||
if (target_poll(target_a9)) {
|
||||
LOG << "FATAL ERROR: Error polling after resume!" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check for halt and trigger event accordingly
|
||||
if (target_a9->state == TARGET_HALTED) {
|
||||
|
||||
uint32_t pc = getCurrentPC();
|
||||
|
||||
switch (target_a9->debug_reason) {
|
||||
case DBG_REASON_WPTANDBKPT:
|
||||
/* Fall through */
|
||||
case DBG_REASON_BREAKPOINT:
|
||||
fail::simulator.onBreakpoint(NULL, pc, fail::ANY_ADDR);
|
||||
if (target_a9->debug_reason == DBG_REASON_BREAKPOINT) {
|
||||
break;
|
||||
}
|
||||
/* Potential fall through */
|
||||
case DBG_REASON_WATCHPOINT:
|
||||
{
|
||||
// ToDo: Replace with calls of every current memory access
|
||||
struct watchpoint *wp = getHaltingWatchpoint();
|
||||
if (!wp) {
|
||||
// ToDo: Determine address by interpreting instruction and register contents
|
||||
LOG << "FATAL ERROR: Can't determine memory-access address of halt cause" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int iswrite;
|
||||
switch (wp->rw) {
|
||||
case WPT_READ:
|
||||
iswrite = 0;
|
||||
break;
|
||||
case WPT_WRITE:
|
||||
iswrite = 1;
|
||||
break;
|
||||
case WPT_ACCESS:
|
||||
// ToDo: Can't tell if read or write
|
||||
iswrite = 1;
|
||||
break;
|
||||
default:
|
||||
LOG << "FATAL ERROR: Can't determine memory-access type of halt cause" << endl;
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
fail::simulator.onMemoryAccess(NULL, wp->address, wp->length, iswrite, pc);
|
||||
}
|
||||
break;
|
||||
case DBG_REASON_SINGLESTEP:
|
||||
LOG << "FATAL ERROR: Single-step is handled in previous loop phase" << endl;
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
LOG << "FATAL ERROR: Target halted in unexpected cpu state!" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute single-step if horizontal hop was detected
|
||||
*/
|
||||
if (target_a9->state == TARGET_HALTED && horizontal_step) {
|
||||
if (target_step(target_a9, 1, 0, 1)) {
|
||||
LOG << "FATAL ERROR: Single-step could not be executed!" << endl;
|
||||
return 1;
|
||||
}
|
||||
// Reset horizontal hop flag
|
||||
horizontal_step = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update timers. Granularity will be coarse, because this is done after polling the device
|
||||
update_timers();
|
||||
}
|
||||
|
||||
/* === FINISHING UP === */
|
||||
unregister_all_commands(cmd_ctx, NULL);
|
||||
|
||||
/* free commandline interface */
|
||||
command_done(cmd_ctx);
|
||||
|
||||
adapter_quit();
|
||||
|
||||
fclose(file);
|
||||
|
||||
LOG << "finished" << endl;
|
||||
|
||||
/*
|
||||
* Return to experiment flow to properly exit execution
|
||||
*/
|
||||
fail::simulator.resume();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void oocdw_set_halt_condition(struct halt_condition *hc)
|
||||
{
|
||||
assert((target_a9->state == TARGET_HALTED) && "Target not halted");
|
||||
assert((hc != NULL) && "No halt condition defined");
|
||||
|
||||
horizontal_step = false;
|
||||
|
||||
if (hc->type == HALT_TYPE_BP) {
|
||||
if (breakpoint_add(target_a9, hc->address,
|
||||
hc->addr_len, BKPT_HARD)) {
|
||||
LOG << "FATAL ERROR: Breakpoint could not be set" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Compare current pc with set breakpoint (potential horizontal hop)
|
||||
if (hc->address == getCurrentPC()) {
|
||||
horizontal_step = true;
|
||||
}
|
||||
} else if (hc->type == HALT_TYPE_SINGLESTEP) {
|
||||
single_step_requested = true;
|
||||
} else {
|
||||
enum watchpoint_rw rw = WPT_ACCESS;
|
||||
if (hc->type == HALT_TYPE_WP_READ) {
|
||||
rw = WPT_READ;
|
||||
} else if (hc->type == HALT_TYPE_WP_WRITE) {
|
||||
rw = WPT_WRITE;
|
||||
} else if (hc->type == HALT_TYPE_WP_READWRITE) {
|
||||
rw = WPT_ACCESS;
|
||||
}
|
||||
|
||||
// No masking => value = 0; mask = 0xffffffff
|
||||
// length const 1, because functional correct and smallest
|
||||
// hit surface
|
||||
if (watchpoint_add(target_a9, hc->address, hc->addr_len,
|
||||
rw, 0x0, 0xffffffff)) {
|
||||
LOG << "FATAL ERROR: Watchpoint could not be set" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Compare current memory access events with set watchpoint
|
||||
// (potential horizontal hop)
|
||||
struct halt_condition mem_accesses [4];
|
||||
getCurrentMemAccesses(mem_accesses);
|
||||
int i = 0;
|
||||
while (mem_accesses[i].address) {
|
||||
// Look for overlapping similar memory access
|
||||
if (mem_accesses[i].type == hc->type) {
|
||||
if (mem_accesses[i].address < hc->address) {
|
||||
if (mem_accesses[i].address + mem_accesses[i].addr_len >= hc->address) {
|
||||
horizontal_step = true;
|
||||
}
|
||||
} else {
|
||||
if (hc->address + hc->addr_len >= mem_accesses[i].address) {
|
||||
horizontal_step = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_delete_halt_condition(struct halt_condition *hc)
|
||||
{
|
||||
assert((target_a9->state == TARGET_HALTED) && "Target not halted");
|
||||
assert((hc != NULL) && "No halt condition defined");
|
||||
|
||||
// Remove halt condition from pandaboard
|
||||
if (hc->type == HALT_TYPE_BP) {
|
||||
breakpoint_remove(target_a9, hc->address);
|
||||
} else if (hc->type == HALT_TYPE_SINGLESTEP) {
|
||||
// Do nothing. Single-stepping event hits one time and
|
||||
// extinguishes itself automatically
|
||||
} else if ((hc->type == HALT_TYPE_WP_READWRITE) ||
|
||||
(hc->type == HALT_TYPE_WP_READ) ||
|
||||
(hc->type == HALT_TYPE_WP_WRITE)) {
|
||||
watchpoint_remove(target_a9, hc->address);
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_halt_target()
|
||||
{
|
||||
if (target_halt(target_a9)) {
|
||||
LOG << "FATAL ERROR: Target could not be halted" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for target to actually stop.
|
||||
*/
|
||||
long long then = timeval_ms();
|
||||
if (target_poll(target_a9)) {
|
||||
LOG << "FATAL ERROR: Target polling failed" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
while (target_a9->state != TARGET_HALTED) {
|
||||
if (target_poll(target_a9)) {
|
||||
LOG << "FATAL ERROR: Target polling failed" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
if (timeval_ms() > then + 1000) {
|
||||
LOG << "FATAL ERROR: Timeout waiting for target halt" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* As "reset halt" and "reset init" fail irregularly on the pandaboard, resulting in
|
||||
* a device freeze, from which only a manual reset can recover the state, a different
|
||||
* approach is used to reset the device and navigate to main entry.
|
||||
* A "reset run" command is executed, which does not cause a device freeze.
|
||||
* Afterwards the pandaboard is immediately halted, so the halt normally triggers
|
||||
* in the initialization phase. Afterwards a breakpoint is set on the target
|
||||
* instruction, so the device is set for navigation to the target dynamic instructions.
|
||||
*
|
||||
* 1) The indirection of navigating to the main entry is needed, because
|
||||
* the first halt condition might be a watchpoint, which could also
|
||||
* trigger in the binary loading phase.
|
||||
* 2) Because it is not guaranteed, that the immediate halt is triggered
|
||||
* before main entry, a "safety loop" is executed before the main
|
||||
* entry. It is therefore necessary to first navigate into this loop
|
||||
* and then jump over it by modifying the program counter.
|
||||
*/
|
||||
void oocdw_reboot()
|
||||
{
|
||||
int retval, reboot_success = 0;
|
||||
|
||||
while (!reboot_success) {
|
||||
LOG << "Rebooting device" << endl;
|
||||
reboot_success = 1;
|
||||
|
||||
// If target is not halted, reset will result in freeze
|
||||
if (target_a9->state != TARGET_HALTED) {
|
||||
oocdw_halt_target();
|
||||
}
|
||||
|
||||
/*
|
||||
* The actual reset command executed by OpenOCD jimtcl-engine
|
||||
*/
|
||||
retval = Jim_Eval(cmd_ctx->interp, "reset");
|
||||
|
||||
retval = target_call_timer_callbacks_now();
|
||||
if (retval) {
|
||||
LOG << "target_call_timer_callbacks_now() Error" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
struct target *target;
|
||||
for (target = all_targets; target; target = target->next)
|
||||
target->type->check_reset(target);
|
||||
|
||||
// Immediate halt after reset.
|
||||
oocdw_halt_target();
|
||||
|
||||
uint32_t pc = buf_get_u32(arm_a9->pc->value, 0, 32);
|
||||
if (pc < 0x8300006c || pc > 0x83000088) {
|
||||
LOG << "NOT IN LOOP!!! PC: " << hex << pc << dec << std::endl;
|
||||
} else {
|
||||
LOG << "Stopped in loop. PC: " << hex << pc << dec << std::endl;
|
||||
}
|
||||
|
||||
// BP on entering main
|
||||
struct halt_condition hc;
|
||||
|
||||
// ToDo: Non static MAIN ENTRY
|
||||
hc.address = 0x83000088;
|
||||
hc.addr_len = 4;
|
||||
hc.type = HALT_TYPE_BP;
|
||||
oocdw_set_halt_condition(&hc);
|
||||
|
||||
if (target_resume(target_a9, 1, 0, 1, 1)) {
|
||||
LOG << "FATAL ERROR: Target could not be resumed" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
long long then;
|
||||
then = timeval_ms();
|
||||
while (target_a9->state != TARGET_HALTED) {
|
||||
int retval = target_poll(target_a9);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG << "FATAL ERROR: Target polling failed" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
// ToDo: Adjust timeout
|
||||
if (timeval_ms() > then + 1000) {
|
||||
LOG << "Error: Timeout waiting for main entry" << endl;
|
||||
reboot_success = 0;
|
||||
static int fail_counter = 1;
|
||||
if (fail_counter++ > 4) {
|
||||
LOG << "FATAL ERROR: Rebooting not possible" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
oocdw_halt_target();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Remove temporary
|
||||
oocdw_delete_halt_condition(&hc);
|
||||
|
||||
if (reboot_success) {
|
||||
// Jump over safety loop (set PC)
|
||||
oocdw_write_reg(15, ARM_REGS_CORE, 0x8300008c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_read_reg(uint32_t reg_num, enum arm_reg_group rg, uint32_t *data)
|
||||
{
|
||||
assert((target_a9->state == TARGET_HALTED) && "Target not halted");
|
||||
|
||||
struct arm *arm = (struct arm*)(target_a9->arch_info);
|
||||
if (rg == ARM_REGS_CORE) {
|
||||
struct reg *reg = arm->core_cache->reg_list + reg_num;
|
||||
|
||||
// Core registers
|
||||
if (arm->read_core_reg(target_a9, reg, reg_num, arm->core_mode)) {
|
||||
LOG << "FATAL ERROR: Could not read register " << reg_num << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
*data = *((uint32_t*)(reg->value));
|
||||
} else {
|
||||
// ToDo:
|
||||
// coprocessor registers
|
||||
/*
|
||||
* struct target *target, int cpnum,
|
||||
uint32_t op1, uint32_t op2,
|
||||
uint32_t CRn, uint32_t CRm,
|
||||
uint32_t *value
|
||||
*/
|
||||
/*if (arm->mrc(...)) {
|
||||
|
||||
}*/
|
||||
LOG << "FATAL ERROR: Accessing coprocessor registers not implemented yet." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_write_reg(uint32_t reg_num, enum arm_reg_group rg, uint32_t data)
|
||||
{
|
||||
assert((target_a9->state == TARGET_HALTED) && "Target not halted");
|
||||
|
||||
struct arm *arm = (struct arm*)(target_a9->arch_info);
|
||||
if (rg == ARM_REGS_CORE) {
|
||||
struct reg *reg = arm->core_cache->reg_list + reg_num;
|
||||
|
||||
// Core registers
|
||||
if (arm->write_core_reg(target_a9, reg, reg_num, arm->core_mode, data)) {
|
||||
LOG << "FATAL ERROR: Could not write register " << reg_num << endl;
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
// ToDo:
|
||||
// coprocessor registers
|
||||
/*
|
||||
* struct target *target, int cpnum,
|
||||
uint32_t op1, uint32_t op2,
|
||||
uint32_t CRn, uint32_t CRm,
|
||||
uint32_t value
|
||||
*/
|
||||
/*if (arm->mcr(...)) {
|
||||
|
||||
|
||||
}*/
|
||||
LOG << "FATAL ERROR: Accessing coprocessor registers not implemented yet." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_finish()
|
||||
{
|
||||
oocdw_exection_finished = true;
|
||||
}
|
||||
|
||||
void oocdw_read_from_memory(uint32_t address, uint32_t chunk_size,
|
||||
uint32_t chunk_num, uint8_t *data)
|
||||
{
|
||||
if (target_read_memory(target_a9, address, chunk_size, chunk_num, data)) {
|
||||
LOG << "FATAL ERROR: Reading from memory failed." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void oocdw_write_to_memory(uint32_t address, uint32_t chunk_size,
|
||||
uint32_t chunk_num, uint8_t const *data,
|
||||
bool cache_inval)
|
||||
{
|
||||
struct target *write_target;
|
||||
if (cache_inval) {
|
||||
// A9 writes and invalidates
|
||||
write_target = target_a9;
|
||||
} else {
|
||||
// M3 writes and does not invalidate
|
||||
write_target = target_m3;
|
||||
}
|
||||
|
||||
if (target_write_memory(write_target, address, chunk_size, chunk_num, data)) {
|
||||
LOG << "FATAL ERROR: Writing to memory failed." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int oocdw_register_timer(void *this_ptr, p_timer_handler_t funct, uint64_t useconds,
|
||||
bool active, const char *id)
|
||||
{
|
||||
for (int i=0; i<P_MAX_TIMERS; i++ ) {
|
||||
// find unused timer
|
||||
if (!timers[i].inUse) {
|
||||
timers[i].this_ptr = this_ptr;
|
||||
timers[i].funct = funct;
|
||||
timers[i].timeToFire = useconds;
|
||||
gettimeofday(&(timers[i].time_begin), NULL);
|
||||
timers[i].active = active;
|
||||
strcpy(timers[i].id, id);
|
||||
timers[i].inUse = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool oocdw_unregisterTimer(unsigned timerID)
|
||||
{
|
||||
if (!timers[timerID].inUse) {
|
||||
return false;
|
||||
}
|
||||
timers[timerID].inUse = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void oocdw_deactivate_timer(unsigned timer_index)
|
||||
{
|
||||
timers[timer_index].active = false;
|
||||
}
|
||||
|
||||
static void update_timers()
|
||||
{
|
||||
for (int i=0; i<P_MAX_TIMERS; i++ ) {
|
||||
if (timers[i].inUse && timers[i].active) {
|
||||
struct timeval t_now;
|
||||
gettimeofday(&t_now, NULL);
|
||||
uint64_t useconds_delta = (t_now.tv_sec - timers[i].time_begin.tv_sec) * 1000000 + t_now.tv_usec - timers[i].time_begin.tv_usec;
|
||||
|
||||
if (timers[i].timeToFire <= useconds_delta) {
|
||||
// Halt target to get defined halted state at experiment end
|
||||
oocdw_halt_target();
|
||||
// Fire
|
||||
timers[i].funct(timers[i].this_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t getCurrentPC()
|
||||
{
|
||||
return buf_get_u32(arm_a9->pc->value, 0, 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all memory access events of current instruction in
|
||||
* 0-terminated (0 in address field) array of max length.
|
||||
* TODO implement analysis of current instruction in combination
|
||||
* with register contents
|
||||
*/
|
||||
static void getCurrentMemAccesses(struct halt_condition *accesses)
|
||||
{
|
||||
// ToDo: Get all 1-byte memory access events of current
|
||||
// instruction. For now return empty array.
|
||||
accesses[0].address = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If only one watchpoint is active, this checkpoint gets returned by
|
||||
* this function
|
||||
*/
|
||||
static struct watchpoint *getHaltingWatchpoint()
|
||||
{
|
||||
struct watchpoint *watchpoint = target_a9->watchpoints;
|
||||
|
||||
if (watchpoint->next) {
|
||||
// Multiple watchpoints activated? No single answer possible
|
||||
return NULL;
|
||||
}
|
||||
return watchpoint;
|
||||
}
|
||||
141
debuggers/openocd/openocd_wrapper.hpp.in
Normal file
141
debuggers/openocd/openocd_wrapper.hpp.in
Normal file
@ -0,0 +1,141 @@
|
||||
#ifndef __OOCD_WRAPPER_H_
|
||||
#define __OOCD_WRAPPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define OOCD_CONF_FILE_PATH "@OOCD_CONF_FILE_PATH@"
|
||||
#define OOCD_CONF_FILES_PATH "@OOCD_CONF_FILES_PATH@"
|
||||
|
||||
enum arm_reg_group {
|
||||
ARM_REGS_CORE,
|
||||
ARM_REGS_COPROCESSOR,
|
||||
};
|
||||
|
||||
enum halt_type {
|
||||
HALT_TYPE_BP,
|
||||
HALT_TYPE_WP_READWRITE,
|
||||
HALT_TYPE_WP_READ,
|
||||
HALT_TYPE_WP_WRITE,
|
||||
HALT_TYPE_SINGLESTEP,
|
||||
};
|
||||
|
||||
struct halt_condition {
|
||||
enum halt_type type;
|
||||
uint32_t address;
|
||||
uint32_t addr_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Read register value
|
||||
* Reads the value of the register defined by \a regnum.
|
||||
* @param reg_num the target register as defined in the ArmArchitecture
|
||||
* @param rg Definition of register group of register defined by \a reg_num
|
||||
* @param data pointer to data as return value
|
||||
*/
|
||||
void oocdw_read_reg(uint32_t reg_num, enum arm_reg_group rg, uint32_t *data);
|
||||
|
||||
/*
|
||||
* Write register value
|
||||
* Writes the value of the register defined by \a regnum.
|
||||
* @param reg_num the target register as defined in the ArmArchitecture
|
||||
* @param rg Definition of register group of register defined by \a reg_num
|
||||
* @param data data to be written
|
||||
*/
|
||||
void oocdw_write_reg(uint32_t reg_num, enum arm_reg_group rg, uint32_t data);
|
||||
|
||||
/*
|
||||
* Set a halt condition
|
||||
*
|
||||
* Halt conditions can be Break- and Watchpoints as well as single-steps
|
||||
* @param hc Pointer to struct defining new halt condition
|
||||
*/
|
||||
void oocdw_set_halt_condition(struct halt_condition *hc);
|
||||
|
||||
/*
|
||||
* Remove a halt condition
|
||||
*
|
||||
* Halt conditions can be Break- and Watchpoints as well as single-steps
|
||||
* @param hc Pointer to struct defining to be deleted halt condition
|
||||
*/
|
||||
void oocdw_delete_halt_condition(struct halt_condition *hc);
|
||||
|
||||
/*
|
||||
* Immediate target halt without sepcific target instruction
|
||||
*/
|
||||
void oocdw_halt_target();
|
||||
|
||||
/*
|
||||
* Target reboot
|
||||
*
|
||||
* The target gets reset and will be navigated to a dynamic instruction
|
||||
* right before main entry. Afterwards a new experiment can be executed.
|
||||
*/
|
||||
void oocdw_reboot();
|
||||
|
||||
/*
|
||||
* Finish OpenOCD execution
|
||||
*
|
||||
* Function will be called by simulator.terminate() and will simply
|
||||
* set a finish-flag. Afterwards a coroutine-switch must be called, so
|
||||
* the actual finishing will be done in the OpenOCD-Wrapper coroutine.
|
||||
* Before returning, another coroutine-switch is called, so the
|
||||
* experiment is able to exit in desired state.
|
||||
*/
|
||||
void oocdw_finish();
|
||||
|
||||
/*
|
||||
* Read data from pandaboard memory
|
||||
*
|
||||
* @param address Start address of memory region to be read
|
||||
* @param chunk_size Size of chunks, which will be read
|
||||
* @param chunk_num Number of chunks to be read
|
||||
* @param data Pointer to read destination
|
||||
*/
|
||||
void oocdw_read_from_memory(uint32_t address, uint32_t chunk_size,
|
||||
uint32_t chunk_num, uint8_t *data);
|
||||
|
||||
/*
|
||||
* Write data to pandaboard memory
|
||||
*
|
||||
* @param address Start address of memory region to be written
|
||||
* @param chunk_size Size of chunks, which will be written
|
||||
* @param chunk_num Number of chunks to be written
|
||||
* @param data Pointer to read source
|
||||
* @param cache_inval Defines if a write to memory should invalidate
|
||||
* the associated cache line.
|
||||
*/
|
||||
void oocdw_write_to_memory(uint32_t address, uint32_t chunk_size,
|
||||
uint32_t chunk_num, uint8_t const *data,
|
||||
bool cache_inval);
|
||||
|
||||
typedef void (*p_timer_handler_t)(void *);
|
||||
/*
|
||||
* Register new timer
|
||||
*
|
||||
* @param this_ptr This pointer for calling object method
|
||||
* @param funct Callback to call if timer expired (object method)
|
||||
* @param useconds Number of microseconds until timer expires
|
||||
* @param active Sets if timer is initially active
|
||||
* @param id String representation of timer
|
||||
* @returns Timer index (ID) or -1 if no timer slot left
|
||||
*/
|
||||
int oocdw_register_timer(void *this_ptr, p_timer_handler_t funct,
|
||||
uint64_t useconds, bool active, const char *id);
|
||||
|
||||
/*
|
||||
* Unregister timer
|
||||
*
|
||||
* @param timerID Timer index (ID), which defines the timer to be
|
||||
* unregistered
|
||||
*/
|
||||
bool oocdw_unregisterTimer(unsigned timerID);
|
||||
|
||||
/*
|
||||
* Deactivate timer
|
||||
*
|
||||
* @param timerID Timer index (ID), which defines the timer to be
|
||||
* deactivated.
|
||||
*/
|
||||
void oocdw_deactivate_timer(unsigned timer_index);
|
||||
|
||||
#endif // __OOCD_WRAPPER_H_
|
||||
Reference in New Issue
Block a user