Merge remote-tracking branch 'origin/riscv' into riscv-compliance-rebase

This commit is contained in:
Megan Wachs
2018-08-29 15:45:11 -07:00
189 changed files with 22856 additions and 1704 deletions
+6 -9
View File
@@ -4,7 +4,9 @@ else
OOCD_TRACE_FILES =
endif
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
%D%/riscv/libriscv.la
STARTUP_TCL_SRCS += %D%/startup.tcl
@@ -21,7 +23,6 @@ noinst_LTLIBRARIES += %D%/libtarget.la
$(NDS32_SRC) \
$(STM8_SRC) \
$(INTEL_IA32_SRC) \
$(RISCV_SRC) \
%D%/avrt.c \
%D%/dsp563xx.c \
%D%/dsp563xx_once.c \
@@ -40,6 +41,7 @@ TARGET_CORE_SRC = \
%D%/target.c \
%D%/target_request.c \
%D%/testee.c \
%D%/semihosting_common.c \
%D%/smp.c
ARMV4_5_SRC = \
@@ -136,13 +138,6 @@ INTEL_IA32_SRC = \
%D%/lakemont.c \
%D%/x86_32_common.c
RISCV_SRC = \
%D%/riscv/riscv-011.c \
%D%/riscv/riscv-013.c \
%D%/riscv/riscv.c \
%D%/riscv/program.c \
%D%/riscv/batch.c
%C%_libtarget_la_SOURCES += \
%D%/algorithm.h \
%D%/arm.h \
@@ -218,9 +213,11 @@ RISCV_SRC = \
%D%/nds32_v3.h \
%D%/nds32_v3m.h \
%D%/nds32_aice.h \
%D%/semihosting_common.h \
%D%/stm8.h \
%D%/lakemont.h \
%D%/x86_32_common.h \
%D%/arm_cti.h
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
+171 -3
View File
@@ -28,6 +28,7 @@
#include "target_type.h"
#include "armv8_opcodes.h"
#include "armv8_cache.h"
#include "arm_semihosting.h"
#include <helper/time_support.h>
enum restart_mode {
@@ -522,6 +523,9 @@ static int aarch64_poll(struct target *target)
if (target->smp)
update_halt_gdb(target, debug_reason);
if (arm_semihosting(target, &retval) != 0)
return retval;
switch (prev_target_state) {
case TARGET_RUNNING:
case TARGET_UNKNOWN:
@@ -543,6 +547,9 @@ static int aarch64_poll(struct target *target)
static int aarch64_halt(struct target *target)
{
struct armv8_common *armv8 = target_to_armv8(target);
armv8->last_run_control_op = ARMV8_RUNCONTROL_HALT;
if (target->smp)
return aarch64_halt_smp(target, false);
@@ -831,6 +838,9 @@ static int aarch64_resume(struct target *target, int current,
int retval = 0;
uint64_t addr = address;
struct armv8_common *armv8 = target_to_armv8(target);
armv8->last_run_control_op = ARMV8_RUNCONTROL_RESUME;
if (target->state != TARGET_HALTED)
return ERROR_TARGET_NOT_HALTED;
@@ -1069,6 +1079,8 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres
int retval;
uint32_t edecr;
armv8->last_run_control_op = ARMV8_RUNCONTROL_STEP;
if (target->state != TARGET_HALTED) {
LOG_WARNING("target not halted");
return ERROR_TARGET_NOT_HALTED;
@@ -1682,17 +1694,19 @@ static int aarch64_deassert_reset(struct target *target)
if (retval != ERROR_OK)
return retval;
retval = aarch64_init_debug_access(target);
if (retval != ERROR_OK)
return retval;
if (target->reset_halt) {
if (target->state != TARGET_HALTED) {
LOG_WARNING("%s: ran after reset and before halt ...",
target_name(target));
retval = target_halt(target);
if (retval != ERROR_OK)
return retval;
}
}
return aarch64_init_debug_access(target);
return retval;
}
static int aarch64_write_cpu_memory_slow(struct target *target,
@@ -2351,6 +2365,7 @@ static int aarch64_init_target(struct command_context *cmd_ctx,
struct target *target)
{
/* examine_first() does a bunch of this */
arm_semihosting_init(target);
return ERROR_OK;
}
@@ -2590,6 +2605,143 @@ COMMAND_HANDLER(aarch64_mask_interrupts_command)
return ERROR_OK;
}
static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
struct command_context *context;
struct target *target;
struct arm *arm;
int retval;
bool is_mcr = false;
int arg_cnt = 0;
if (Jim_CompareStringImmediate(interp, argv[0], "mcr")) {
is_mcr = true;
arg_cnt = 7;
} else {
arg_cnt = 6;
}
context = current_command_context(interp);
assert(context != NULL);
target = get_current_target(context);
if (target == NULL) {
LOG_ERROR("%s: no current target", __func__);
return JIM_ERR;
}
if (!target_was_examined(target)) {
LOG_ERROR("%s: not yet examined", target_name(target));
return JIM_ERR;
}
arm = target_to_arm(target);
if (!is_arm(arm)) {
LOG_ERROR("%s: not an ARM", target_name(target));
return JIM_ERR;
}
if (target->state != TARGET_HALTED)
return ERROR_TARGET_NOT_HALTED;
if (arm->core_state == ARM_STATE_AARCH64) {
LOG_ERROR("%s: not 32-bit arm target", target_name(target));
return JIM_ERR;
}
if (argc != arg_cnt) {
LOG_ERROR("%s: wrong number of arguments", __func__);
return JIM_ERR;
}
int cpnum;
uint32_t op1;
uint32_t op2;
uint32_t CRn;
uint32_t CRm;
uint32_t value;
long l;
/* NOTE: parameter sequence matches ARM instruction set usage:
* MCR pNUM, op1, rX, CRn, CRm, op2 ; write CP from rX
* MRC pNUM, op1, rX, CRn, CRm, op2 ; read CP into rX
* The "rX" is necessarily omitted; it uses Tcl mechanisms.
*/
retval = Jim_GetLong(interp, argv[1], &l);
if (retval != JIM_OK)
return retval;
if (l & ~0xf) {
LOG_ERROR("%s: %s %d out of range", __func__,
"coprocessor", (int) l);
return JIM_ERR;
}
cpnum = l;
retval = Jim_GetLong(interp, argv[2], &l);
if (retval != JIM_OK)
return retval;
if (l & ~0x7) {
LOG_ERROR("%s: %s %d out of range", __func__,
"op1", (int) l);
return JIM_ERR;
}
op1 = l;
retval = Jim_GetLong(interp, argv[3], &l);
if (retval != JIM_OK)
return retval;
if (l & ~0xf) {
LOG_ERROR("%s: %s %d out of range", __func__,
"CRn", (int) l);
return JIM_ERR;
}
CRn = l;
retval = Jim_GetLong(interp, argv[4], &l);
if (retval != JIM_OK)
return retval;
if (l & ~0xf) {
LOG_ERROR("%s: %s %d out of range", __func__,
"CRm", (int) l);
return JIM_ERR;
}
CRm = l;
retval = Jim_GetLong(interp, argv[5], &l);
if (retval != JIM_OK)
return retval;
if (l & ~0x7) {
LOG_ERROR("%s: %s %d out of range", __func__,
"op2", (int) l);
return JIM_ERR;
}
op2 = l;
value = 0;
if (is_mcr == true) {
retval = Jim_GetLong(interp, argv[6], &l);
if (retval != JIM_OK)
return retval;
value = l;
/* NOTE: parameters reordered! */
/* ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2) */
retval = arm->mcr(target, cpnum, op1, op2, CRn, CRm, value);
if (retval != ERROR_OK)
return JIM_ERR;
} else {
/* NOTE: parameters reordered! */
/* ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2) */
retval = arm->mrc(target, cpnum, op1, op2, CRn, CRm, &value);
if (retval != ERROR_OK)
return JIM_ERR;
Jim_SetResult(interp, Jim_NewIntObj(interp, value));
}
return JIM_OK;
}
static const struct command_registration aarch64_exec_command_handlers[] = {
{
.name = "cache_info",
@@ -2625,9 +2777,25 @@ static const struct command_registration aarch64_exec_command_handlers[] = {
.help = "mask aarch64 interrupts during single-step",
.usage = "['on'|'off']",
},
{
.name = "mcr",
.mode = COMMAND_EXEC,
.jim_handler = jim_mcrmrc,
.help = "write coprocessor register",
.usage = "cpnum op1 CRn CRm op2 value",
},
{
.name = "mrc",
.mode = COMMAND_EXEC,
.jim_handler = jim_mcrmrc,
.help = "read coprocessor register",
.usage = "cpnum op1 CRn CRm op2",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration aarch64_command_handlers[] = {
{
.chain = armv8_command_handlers,
+11
View File
@@ -276,6 +276,16 @@ static int swd_run(struct adiv5_dap *dap)
return swd_run_inner(dap);
}
/** Put the SWJ-DP back to JTAG mode */
static void swd_quit(struct adiv5_dap *dap)
{
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
swd->switch_seq(SWD_TO_JTAG);
/* flush the queue before exit */
swd->run();
}
const struct dap_ops swd_dap_ops = {
.connect = swd_connect,
.queue_dp_read = swd_queue_dp_read,
@@ -284,6 +294,7 @@ const struct dap_ops swd_dap_ops = {
.queue_ap_write = swd_queue_ap_write,
.queue_ap_abort = swd_queue_ap_abort,
.run = swd_run,
.quit = swd_quit,
};
/*
+3 -22
View File
@@ -8,6 +8,9 @@
* Copyright (C) 2009 by Øyvind Harboe
* oyvind.harboe@zylin.com
*
* Copyright (C) 2018 by Liviu Ionescu
* <ilg@livius.net>
*
* 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 2 of the License, or
@@ -28,7 +31,6 @@
#include <helper/command.h>
#include "target.h"
/**
* @file
* Holds the interface to ARM cores.
@@ -181,32 +183,11 @@ struct arm {
/** Flag reporting armv6m based core. */
bool is_armv6m;
/** Flag reporting whether semihosting is active. */
bool is_semihosting;
/** Flag reporting whether semihosting fileio is active. */
bool is_semihosting_fileio;
/** Flag reporting whether semihosting fileio operation is active. */
bool semihosting_hit_fileio;
/** Floating point or VFP version, 0 if disabled. */
int arm_vfp_version;
/** Current semihosting operation. */
int semihosting_op;
/** Current semihosting result. */
int semihosting_result;
/** Value to be returned by semihosting SYS_ERRNO request. */
int semihosting_errno;
int (*setup_semihosting)(struct target *target, int enable);
/** Semihosting command line. */
char *semihosting_cmdline;
/** Backpointer to the target. */
struct target *target;
+69 -22
View File
@@ -102,8 +102,10 @@ static int mem_ap_setup_csw(struct adiv5_ap *ap, uint32_t csw)
if (csw != ap->csw_value) {
/* LOG_DEBUG("DAP: Set CSW %x",csw); */
int retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
if (retval != ERROR_OK)
if (retval != ERROR_OK) {
ap->csw_value = 0;
return retval;
}
ap->csw_value = csw;
}
return ERROR_OK;
@@ -114,8 +116,10 @@ static int mem_ap_setup_tar(struct adiv5_ap *ap, uint32_t tar)
if (!ap->tar_valid || tar != ap->tar_value) {
/* LOG_DEBUG("DAP: Set TAR %x",tar); */
int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR, tar);
if (retval != ERROR_OK)
if (retval != ERROR_OK) {
ap->tar_valid = false;
return retval;
}
ap->tar_value = tar;
ap->tar_valid = true;
}
@@ -152,6 +156,8 @@ static uint32_t mem_ap_get_tar_increment(struct adiv5_ap *ap)
return 2;
case CSW_32BIT:
return 4;
default:
return 0;
}
case CSW_ADDRINC_PACKED:
return 4;
@@ -1610,13 +1616,12 @@ COMMAND_HANDLER(dap_memaccess_command)
COMMAND_HANDLER(dap_apsel_command)
{
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel, apid;
int retval;
uint32_t apsel;
switch (CMD_ARGC) {
case 0:
apsel = dap->apsel;
break;
command_print(CMD_CTX, "%" PRIi32, dap->apsel);
return ERROR_OK;
case 1:
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
/* AP address is in bits 31:24 of DP_SELECT */
@@ -1628,18 +1633,7 @@ COMMAND_HANDLER(dap_apsel_command)
}
dap->apsel = apsel;
retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR, &apid);
if (retval != ERROR_OK)
return retval;
retval = dap_run(dap);
if (retval != ERROR_OK)
return retval;
command_print(CMD_CTX, "ap %" PRIi32 " selected, identification register 0x%8.8" PRIx32,
apsel, apid);
return retval;
return ERROR_OK;
}
COMMAND_HANDLER(dap_apcsw_command)
@@ -1720,6 +1714,7 @@ COMMAND_HANDLER(dap_apreg_command)
{
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t apsel, reg, value;
struct adiv5_ap *ap;
int retval;
if (CMD_ARGC < 2 || CMD_ARGC > 3)
@@ -1729,6 +1724,7 @@ COMMAND_HANDLER(dap_apreg_command)
/* AP address is in bits 31:24 of DP_SELECT */
if (apsel >= 256)
return ERROR_COMMAND_SYNTAX_ERROR;
ap = dap_ap(dap, apsel);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg);
if (reg >= 256 || (reg & 3))
@@ -1736,9 +1732,21 @@ COMMAND_HANDLER(dap_apreg_command)
if (CMD_ARGC == 3) {
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
retval = dap_queue_ap_write(dap_ap(dap, apsel), reg, value);
switch (reg) {
case MEM_AP_REG_CSW:
ap->csw_default = 0; /* invalid, force write */
retval = mem_ap_setup_csw(ap, value);
break;
case MEM_AP_REG_TAR:
ap->tar_valid = false; /* invalid, force write */
retval = mem_ap_setup_tar(ap, value);
break;
default:
retval = dap_queue_ap_write(ap, reg, value);
break;
}
} else {
retval = dap_queue_ap_read(dap_ap(dap, apsel), reg, &value);
retval = dap_queue_ap_read(ap, reg, &value);
}
if (retval == ERROR_OK)
retval = dap_run(dap);
@@ -1752,6 +1760,37 @@ COMMAND_HANDLER(dap_apreg_command)
return retval;
}
COMMAND_HANDLER(dap_dpreg_command)
{
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
uint32_t reg, value;
int retval;
if (CMD_ARGC < 1 || CMD_ARGC > 2)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], reg);
if (reg >= 256 || (reg & 3))
return ERROR_COMMAND_SYNTAX_ERROR;
if (CMD_ARGC == 2) {
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
retval = dap_queue_dp_write(dap, reg, value);
} else {
retval = dap_queue_dp_read(dap, reg, &value);
}
if (retval == ERROR_OK)
retval = dap_run(dap);
if (retval != ERROR_OK)
return retval;
if (CMD_ARGC == 1)
command_print(CMD_CTX, "0x%08" PRIx32, value);
return retval;
}
COMMAND_HANDLER(dap_ti_be_32_quirks_command)
{
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
@@ -1787,7 +1826,7 @@ const struct command_registration dap_instance_commands[] = {
{
.name = "apsel",
.handler = dap_apsel_command,
.mode = COMMAND_EXEC,
.mode = COMMAND_ANY,
.help = "Set the currently selected AP (default 0) "
"and display the result",
.usage = "[ap_num]",
@@ -1795,7 +1834,7 @@ const struct command_registration dap_instance_commands[] = {
{
.name = "apcsw",
.handler = dap_apcsw_command,
.mode = COMMAND_EXEC,
.mode = COMMAND_ANY,
.help = "Set CSW default bits",
.usage = "[value [mask]]",
},
@@ -1816,6 +1855,14 @@ const struct command_registration dap_instance_commands[] = {
"(reg is byte address of a word register, like 0 4 8...)",
.usage = "ap_num reg [value]",
},
{
.name = "dpreg",
.handler = dap_dpreg_command,
.mode = COMMAND_EXEC,
.help = "read/write a register from DP "
"(reg is byte address (bank << 4 | reg) of a word register, like 0 4 8...)",
.usage = "reg [value]",
},
{
.name = "baseaddr",
.handler = dap_baseaddr_command,
+3
View File
@@ -286,6 +286,9 @@ struct dap_ops {
/** Executes all queued DAP operations but doesn't check
* sticky error conditions */
int (*sync)(struct adiv5_dap *dap);
/** Optional; called at OpenOCD exit */
void (*quit)(struct adiv5_dap *dap);
};
/*
+5
View File
@@ -132,8 +132,13 @@ static int dap_init_all(void)
int dap_cleanup_all(void)
{
struct arm_dap_object *obj, *tmp;
struct adiv5_dap *dap;
list_for_each_entry_safe(obj, tmp, &all_dap, lh) {
dap = &obj->dap;
if (dap->ops && dap->ops->quit)
dap->ops->quit(dap);
free(obj->name);
free(obj);
}
+72 -9
View File
@@ -118,15 +118,78 @@ static int evaluate_pld(uint32_t opcode,
uint32_t address, struct arm_instruction *instruction)
{
/* PLD */
if ((opcode & 0x0d70f000) == 0x0550f000) {
if ((opcode & 0x0d30f000) == 0x0510f000) {
uint8_t Rn;
uint8_t U;
unsigned offset;
instruction->type = ARM_PLD;
Rn = (opcode & 0xf0000) >> 16;
U = (opcode & 0x00800000) >> 23;
if (Rn == 0xf) {
/* literal */
offset = opcode & 0x0fff;
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD %s%d",
address, opcode, U ? "" : "-", offset);
} else {
uint8_t I, R;
snprintf(instruction->text,
128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...",
address,
opcode);
I = (opcode & 0x02000000) >> 25;
R = (opcode & 0x00400000) >> 22;
if (I) {
/* register PLD{W} [<Rn>,+/-<Rm>{, <shift>}] */
offset = (opcode & 0x0F80) >> 7;
uint8_t Rm;
Rm = opcode & 0xf;
if (offset == 0) {
/* No shift */
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d]",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm);
} else {
uint8_t shift;
shift = (opcode & 0x60) >> 5;
if (shift == 0x0) {
/* LSL */
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSL #0x%x)",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
} else if (shift == 0x1) {
/* LSR */
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSR #0x%x)",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
} else if (shift == 0x2) {
/* ASR */
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ASR #0x%x)",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
} else if (shift == 0x3) {
/* ROR */
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ROR #0x%x)",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
}
}
} else {
/* immediate PLD{W} [<Rn>, #+/-<imm12>] */
offset = opcode & 0x0fff;
if (offset == 0) {
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d]",
address, opcode, R ? "" : "W", Rn);
} else {
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, #%s%d]",
address, opcode, R ? "" : "W", Rn, U ? "" : "-", offset);
}
}
}
return ERROR_OK;
}
/* DSB */
@@ -1549,7 +1612,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMLAW < y> */
if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) {
if (((opcode & 0x00600000) == 0x00200000) && (x == 0)) {
uint8_t Rd, Rm, Rs, Rn;
instruction->type = ARM_SMLAWy;
Rd = (opcode & 0xf0000) >> 16;
@@ -1571,7 +1634,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMUL < x><y> */
if ((opcode & 0x00600000) == 0x00300000) {
if ((opcode & 0x00600000) == 0x00600000) {
uint8_t Rd, Rm, Rs;
instruction->type = ARM_SMULxy;
Rd = (opcode & 0xf0000) >> 16;
@@ -1592,7 +1655,7 @@ static int evaluate_misc_instr(uint32_t opcode,
}
/* SMULW < y> */
if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) {
if (((opcode & 0x00600000) == 0x00200000) && (x == 1)) {
uint8_t Rd, Rm, Rs;
instruction->type = ARM_SMULWy;
Rd = (opcode & 0xf0000) >> 16;
+92 -555
View File
@@ -8,6 +8,9 @@
* Copyright (C) 2016 by Square, Inc. *
* Steven Stallion <stallion@squareup.com> *
* *
* Copyright (C) 2018 by Liviu Ionescu *
* <ilg@livius.net> *
* *
* 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 2 of the License, or *
@@ -43,6 +46,7 @@
#include "arm7_9_common.h"
#include "armv7m.h"
#include "armv7a.h"
#include "armv8.h"
#include "cortex_m.h"
#include "register.h"
#include "arm_opcodes.h"
@@ -52,25 +56,35 @@
#include <helper/log.h>
#include <sys/stat.h>
static const int open_modeflags[12] = {
O_RDONLY,
O_RDONLY | O_BINARY,
O_RDWR,
O_RDWR | O_BINARY,
O_WRONLY | O_CREAT | O_TRUNC,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
O_RDWR | O_CREAT | O_TRUNC,
O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
O_WRONLY | O_CREAT | O_APPEND,
O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
O_RDWR | O_CREAT | O_APPEND,
O_RDWR | O_CREAT | O_APPEND | O_BINARY
};
static int arm_semihosting_resume(struct target *target, int *retval)
{
if (is_armv8(target_to_armv8(target))) {
struct armv8_common *armv8 = target_to_armv8(target);
if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) {
*retval = target_resume(target, 1, 0, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
return 0;
}
} else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP)
target->debug_reason = DBG_REASON_SINGLESTEP;
} else {
*retval = target_resume(target, 1, 0, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
return 0;
}
}
return 1;
}
static int post_result(struct target *target)
{
struct arm *arm = target_to_arm(target);
if (!target->semihosting)
return ERROR_FAIL;
/* REVISIT this looks wrong ... ARM11 and Cortex-A8
* should work this way at least sometimes.
*/
@@ -79,7 +93,7 @@ static int post_result(struct target *target)
uint32_t spsr;
/* return value in R0 */
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
arm->core_cache->reg_list[0].dirty = 1;
/* LR --> PC */
@@ -100,528 +114,28 @@ static int post_result(struct target *target)
if (spsr & 0x20)
arm->core_state = ARM_STATE_THUMB;
} else if (is_armv8(target_to_armv8(target))) {
if (arm->core_state == ARM_STATE_AARCH64) {
/* return value in R0 */
buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
arm->core_cache->reg_list[0].dirty = 1;
uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
buf_set_u64(arm->pc->value, 0, 64, pc + 4);
arm->pc->dirty = 1;
}
} else {
/* resume execution, this will be pc+2 to skip over the
* bkpt instruction */
/* return result in R0 */
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
arm->core_cache->reg_list[0].dirty = 1;
}
return ERROR_OK;
}
static int do_semihosting(struct target *target)
{
struct arm *arm = target_to_arm(target);
struct gdb_fileio_info *fileio_info = target->fileio_info;
uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
uint8_t params[16];
int retval;
/*
* TODO: lots of security issues are not considered yet, such as:
* - no validation on target provided file descriptors
* - no safety checks on opened/deleted/renamed file paths
* Beware the target app you use this support with.
*
* TODO: unsupported semihosting fileio operations could be
* implemented if we had a small working area at our disposal.
*/
switch ((arm->semihosting_op = r0)) {
case 0x01: /* SYS_OPEN */
retval = target_read_memory(target, r1, 4, 3, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t a = target_buffer_get_u32(target, params+0);
uint32_t m = target_buffer_get_u32(target, params+4);
uint32_t l = target_buffer_get_u32(target, params+8);
uint8_t fn[256];
retval = target_read_memory(target, a, 1, l, fn);
if (retval != ERROR_OK)
return retval;
fn[l] = 0;
if (arm->is_semihosting_fileio) {
if (strcmp((char *)fn, ":tt") == 0)
arm->semihosting_result = 0;
else {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "open";
fileio_info->param_1 = a;
fileio_info->param_2 = l;
fileio_info->param_3 = open_modeflags[m];
fileio_info->param_4 = 0644;
}
} else {
if (l <= 255 && m <= 11) {
if (strcmp((char *)fn, ":tt") == 0) {
if (m < 4)
arm->semihosting_result = dup(STDIN_FILENO);
else
arm->semihosting_result = dup(STDOUT_FILENO);
} else {
/* cygwin requires the permission setting
* otherwise it will fail to reopen a previously
* written file */
arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644);
}
arm->semihosting_errno = errno;
} else {
arm->semihosting_result = -1;
arm->semihosting_errno = EINVAL;
}
}
}
break;
case 0x02: /* SYS_CLOSE */
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
else {
int fd = target_buffer_get_u32(target, params+0);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "close";
fileio_info->param_1 = fd;
} else {
arm->semihosting_result = close(fd);
arm->semihosting_errno = errno;
}
}
break;
case 0x03: /* SYS_WRITEC */
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "write";
fileio_info->param_1 = 1;
fileio_info->param_2 = r1;
fileio_info->param_3 = 1;
} else {
unsigned char c;
retval = target_read_memory(target, r1, 1, 1, &c);
if (retval != ERROR_OK)
return retval;
putchar(c);
arm->semihosting_result = 0;
}
break;
case 0x04: /* SYS_WRITE0 */
if (arm->is_semihosting_fileio) {
size_t count = 0;
for (uint32_t a = r1;; a++) {
unsigned char c;
retval = target_read_memory(target, a, 1, 1, &c);
if (retval != ERROR_OK)
return retval;
if (c == '\0')
break;
count++;
}
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "write";
fileio_info->param_1 = 1;
fileio_info->param_2 = r1;
fileio_info->param_3 = count;
} else {
do {
unsigned char c;
retval = target_read_memory(target, r1++, 1, 1, &c);
if (retval != ERROR_OK)
return retval;
if (!c)
break;
putchar(c);
} while (1);
arm->semihosting_result = 0;
}
break;
case 0x05: /* SYS_WRITE */
retval = target_read_memory(target, r1, 4, 3, params);
if (retval != ERROR_OK)
return retval;
else {
int fd = target_buffer_get_u32(target, params+0);
uint32_t a = target_buffer_get_u32(target, params+4);
size_t l = target_buffer_get_u32(target, params+8);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "write";
fileio_info->param_1 = fd;
fileio_info->param_2 = a;
fileio_info->param_3 = l;
} else {
uint8_t *buf = malloc(l);
if (!buf) {
arm->semihosting_result = -1;
arm->semihosting_errno = ENOMEM;
} else {
retval = target_read_buffer(target, a, l, buf);
if (retval != ERROR_OK) {
free(buf);
return retval;
}
arm->semihosting_result = write(fd, buf, l);
arm->semihosting_errno = errno;
if (arm->semihosting_result >= 0)
arm->semihosting_result = l - arm->semihosting_result;
free(buf);
}
}
}
break;
case 0x06: /* SYS_READ */
retval = target_read_memory(target, r1, 4, 3, params);
if (retval != ERROR_OK)
return retval;
else {
int fd = target_buffer_get_u32(target, params+0);
uint32_t a = target_buffer_get_u32(target, params+4);
ssize_t l = target_buffer_get_u32(target, params+8);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "read";
fileio_info->param_1 = fd;
fileio_info->param_2 = a;
fileio_info->param_3 = l;
} else {
uint8_t *buf = malloc(l);
if (!buf) {
arm->semihosting_result = -1;
arm->semihosting_errno = ENOMEM;
} else {
arm->semihosting_result = read(fd, buf, l);
arm->semihosting_errno = errno;
if (arm->semihosting_result >= 0) {
retval = target_write_buffer(target, a, arm->semihosting_result, buf);
if (retval != ERROR_OK) {
free(buf);
return retval;
}
arm->semihosting_result = l - arm->semihosting_result;
}
free(buf);
}
}
}
break;
case 0x07: /* SYS_READC */
if (arm->is_semihosting_fileio) {
LOG_ERROR("SYS_READC not supported by semihosting fileio");
return ERROR_FAIL;
}
arm->semihosting_result = getchar();
break;
case 0x08: /* SYS_ISERROR */
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0);
break;
case 0x09: /* SYS_ISTTY */
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "isatty";
fileio_info->param_1 = r1;
} else {
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0));
}
break;
case 0x0a: /* SYS_SEEK */
retval = target_read_memory(target, r1, 4, 2, params);
if (retval != ERROR_OK)
return retval;
else {
int fd = target_buffer_get_u32(target, params+0);
off_t pos = target_buffer_get_u32(target, params+4);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "lseek";
fileio_info->param_1 = fd;
fileio_info->param_2 = pos;
fileio_info->param_3 = SEEK_SET;
} else {
arm->semihosting_result = lseek(fd, pos, SEEK_SET);
arm->semihosting_errno = errno;
if (arm->semihosting_result == pos)
arm->semihosting_result = 0;
}
}
break;
case 0x0c: /* SYS_FLEN */
if (arm->is_semihosting_fileio) {
LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
return ERROR_FAIL;
}
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
else {
int fd = target_buffer_get_u32(target, params+0);
struct stat buf;
arm->semihosting_result = fstat(fd, &buf);
if (arm->semihosting_result == -1) {
arm->semihosting_errno = errno;
arm->semihosting_result = -1;
break;
}
arm->semihosting_result = buf.st_size;
}
break;
case 0x0e: /* SYS_REMOVE */
retval = target_read_memory(target, r1, 4, 2, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t a = target_buffer_get_u32(target, params+0);
uint32_t l = target_buffer_get_u32(target, params+4);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "unlink";
fileio_info->param_1 = a;
fileio_info->param_2 = l;
} else {
if (l <= 255) {
uint8_t fn[256];
retval = target_read_memory(target, a, 1, l, fn);
if (retval != ERROR_OK)
return retval;
fn[l] = 0;
arm->semihosting_result = remove((char *)fn);
arm->semihosting_errno = errno;
} else {
arm->semihosting_result = -1;
arm->semihosting_errno = EINVAL;
}
}
}
break;
case 0x0f: /* SYS_RENAME */
retval = target_read_memory(target, r1, 4, 4, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t a1 = target_buffer_get_u32(target, params+0);
uint32_t l1 = target_buffer_get_u32(target, params+4);
uint32_t a2 = target_buffer_get_u32(target, params+8);
uint32_t l2 = target_buffer_get_u32(target, params+12);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "rename";
fileio_info->param_1 = a1;
fileio_info->param_2 = l1;
fileio_info->param_3 = a2;
fileio_info->param_4 = l2;
} else {
if (l1 <= 255 && l2 <= 255) {
uint8_t fn1[256], fn2[256];
retval = target_read_memory(target, a1, 1, l1, fn1);
if (retval != ERROR_OK)
return retval;
retval = target_read_memory(target, a2, 1, l2, fn2);
if (retval != ERROR_OK)
return retval;
fn1[l1] = 0;
fn2[l2] = 0;
arm->semihosting_result = rename((char *)fn1, (char *)fn2);
arm->semihosting_errno = errno;
} else {
arm->semihosting_result = -1;
arm->semihosting_errno = EINVAL;
}
}
}
break;
case 0x11: /* SYS_TIME */
arm->semihosting_result = time(NULL);
break;
case 0x13: /* SYS_ERRNO */
arm->semihosting_result = arm->semihosting_errno;
break;
case 0x15: /* SYS_GET_CMDLINE */
retval = target_read_memory(target, r1, 4, 2, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t a = target_buffer_get_u32(target, params+0);
uint32_t l = target_buffer_get_u32(target, params+4);
char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : "";
uint32_t s = strlen(arg) + 1;
if (l < s)
arm->semihosting_result = -1;
else {
retval = target_write_buffer(target, a, s, (uint8_t *)arg);
if (retval != ERROR_OK)
return retval;
arm->semihosting_result = 0;
}
}
break;
case 0x16: /* SYS_HEAPINFO */
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t a = target_buffer_get_u32(target, params+0);
/* tell the remote we have no idea */
memset(params, 0, 4*4);
retval = target_write_memory(target, a, 4, 4, params);
if (retval != ERROR_OK)
return retval;
arm->semihosting_result = 0;
}
break;
case 0x18: /* angel_SWIreason_ReportException */
switch (r1) {
case 0x20026: /* ADP_Stopped_ApplicationExit */
fprintf(stderr, "semihosting: *** application exited ***\n");
break;
case 0x20000: /* ADP_Stopped_BranchThroughZero */
case 0x20001: /* ADP_Stopped_UndefinedInstr */
case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
case 0x20003: /* ADP_Stopped_PrefetchAbort */
case 0x20004: /* ADP_Stopped_DataAbort */
case 0x20005: /* ADP_Stopped_AddressException */
case 0x20006: /* ADP_Stopped_IRQ */
case 0x20007: /* ADP_Stopped_FIQ */
case 0x20020: /* ADP_Stopped_BreakPoint */
case 0x20021: /* ADP_Stopped_WatchPoint */
case 0x20022: /* ADP_Stopped_StepComplete */
case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
case 0x20024: /* ADP_Stopped_InternalError */
case 0x20025: /* ADP_Stopped_UserInterruption */
case 0x20027: /* ADP_Stopped_StackOverflow */
case 0x20028: /* ADP_Stopped_DivisionByZero */
case 0x20029: /* ADP_Stopped_OSSpecific */
default:
fprintf(stderr, "semihosting: exception %#x\n",
(unsigned) r1);
}
return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
case 0x12: /* SYS_SYSTEM */
/* Provide SYS_SYSTEM functionality. Uses the
* libc system command, there may be a reason *NOT*
* to use this, but as I can't think of one, I
* implemented it this way.
*/
retval = target_read_memory(target, r1, 4, 2, params);
if (retval != ERROR_OK)
return retval;
else {
uint32_t len = target_buffer_get_u32(target, params+4);
uint32_t c_ptr = target_buffer_get_u32(target, params);
if (arm->is_semihosting_fileio) {
arm->semihosting_hit_fileio = true;
fileio_info->identifier = "system";
fileio_info->param_1 = c_ptr;
fileio_info->param_2 = len;
} else {
uint8_t cmd[256];
if (len > 255) {
arm->semihosting_result = -1;
arm->semihosting_errno = EINVAL;
} else {
memset(cmd, 0x0, 256);
retval = target_read_memory(target, c_ptr, 1, len, cmd);
if (retval != ERROR_OK)
return retval;
else
arm->semihosting_result = system((const char *)cmd);
}
}
}
break;
case 0x0d: /* SYS_TMPNAM */
case 0x10: /* SYS_CLOCK */
case 0x17: /* angel_SWIreason_EnterSVC */
case 0x30: /* SYS_ELAPSED */
case 0x31: /* SYS_TICKFREQ */
default:
fprintf(stderr, "semihosting: unsupported call %#x\n",
(unsigned) r0);
arm->semihosting_result = -1;
arm->semihosting_errno = ENOTSUP;
}
return ERROR_OK;
}
static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
{
struct arm *arm = target_to_arm(target);
/* To avoid uneccessary duplication, semihosting prepares the
* fileio_info structure out-of-band when the target halts. See
* do_semihosting for more detail.
*/
if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio)
return ERROR_FAIL;
return ERROR_OK;
}
static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c)
{
struct arm *arm = target_to_arm(target);
struct gdb_fileio_info *fileio_info = target->fileio_info;
/* clear pending status */
arm->semihosting_hit_fileio = false;
arm->semihosting_result = result;
arm->semihosting_errno = fileio_errno;
/* Some fileio results do not match up with what the semihosting
* operation expects; for these operations, we munge the results
* below:
*/
switch (arm->semihosting_op) {
case 0x05: /* SYS_WRITE */
if (result < 0)
arm->semihosting_result = fileio_info->param_3;
else
arm->semihosting_result = 0;
break;
case 0x06: /* SYS_READ */
if (result == (int)fileio_info->param_3)
arm->semihosting_result = 0;
if (result <= 0)
arm->semihosting_result = fileio_info->param_3;
break;
case 0x0a: /* SYS_SEEK */
if (result > 0)
arm->semihosting_result = 0;
break;
}
return post_result(target);
}
/**
* Initialize ARM semihosting support.
*
@@ -630,14 +144,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b
*/
int arm_semihosting_init(struct target *target)
{
target->fileio_info = malloc(sizeof(*target->fileio_info));
if (target->fileio_info == NULL) {
LOG_ERROR("out of memory");
return ERROR_FAIL;
}
target->type->get_gdb_fileio_info = get_gdb_fileio_info;
target->type->gdb_fileio_end = gdb_fileio_end;
struct arm *arm = target_to_arm(target);
assert(arm->setup_semihosting);
semihosting_common_init(target, arm->setup_semihosting, post_result);
return ERROR_OK;
}
@@ -662,7 +171,11 @@ int arm_semihosting(struct target *target, int *retval)
uint32_t pc, lr, spsr;
struct reg *r;
if (!arm->is_semihosting)
struct semihosting *semihosting = target->semihosting;
if (!semihosting)
return 0;
if (!semihosting->is_active)
return 0;
if (is_arm7_9(target_to_arm7_9(target)) ||
@@ -758,6 +271,24 @@ int arm_semihosting(struct target *target, int *retval)
/* bkpt 0xAB */
if (insn != 0xBEAB)
return 0;
} else if (is_armv8(target_to_armv8(target))) {
if (target->debug_reason != DBG_REASON_BREAKPOINT)
return 0;
if (arm->core_state == ARM_STATE_AARCH64) {
uint32_t insn = 0;
r = arm->pc;
uint64_t pc64 = buf_get_u64(r->value, 0, 64);
*retval = target_read_u32(target, pc64, &insn);
if (*retval != ERROR_OK)
return 1;
/* bkpt 0xAB */
if (insn != 0xD45E0000)
return 0;
} else
return 1;
} else {
LOG_ERROR("Unsupported semi-hosting Target");
return 0;
@@ -766,32 +297,38 @@ int arm_semihosting(struct target *target, int *retval)
/* Perform semihosting if we are not waiting on a fileio
* operation to complete.
*/
if (!arm->semihosting_hit_fileio) {
*retval = do_semihosting(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed semihosting operation");
if (!semihosting->hit_fileio) {
if (is_armv8(target_to_armv8(target)) &&
arm->core_state == ARM_STATE_AARCH64) {
/* Read op and param from register x0 and x1 respectively. */
semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
semihosting->word_size_bytes = 8;
} else {
/* Read op and param from register r0 and r1 respectively. */
semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
semihosting->word_size_bytes = 4;
}
/* Check for ARM operation numbers. */
if (0 <= semihosting->op && semihosting->op <= 0x31) {
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed semihosting operation");
return 0;
}
} else {
/* Unknown operation number, not a semihosting call. */
return 0;
}
}
/* Post result to target if we are not waiting on a fileio
/* Resume if target it is resumable and we are not waiting on a fileio
* operation to complete:
*/
if (!arm->semihosting_hit_fileio) {
*retval = post_result(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to post semihosting result");
return 0;
}
*retval = target_resume(target, 1, 0, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
return 0;
}
return 1;
}
if (semihosting->is_resumable && !semihosting->hit_fileio)
return arm_semihosting_resume(target, retval);
return 0;
}
+2
View File
@@ -19,6 +19,8 @@
#ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H
#define OPENOCD_TARGET_ARM_SEMIHOSTING_H
#include "semihosting_common.h"
int arm_semihosting_init(struct target *target);
int arm_semihosting(struct target *target, int *retval);
+21 -120
View File
@@ -8,6 +8,9 @@
* Copyright (C) 2008 by Oyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* Copyright (C) 2018 by Liviu Ionescu *
* <ilg@livius.net> *
* *
* 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 2 of the License, or *
@@ -34,6 +37,7 @@
#include <helper/binarybuffer.h>
#include "algorithm.h"
#include "register.h"
#include "semihosting_common.h"
/* offsets into armv4_5 core register cache */
enum {
@@ -748,7 +752,7 @@ int arm_arch_state(struct target *target)
}
/* avoid filling log waiting for fileio reply */
if (arm->semihosting_hit_fileio)
if (target->semihosting && target->semihosting->hit_fileio)
return ERROR_OK;
LOG_USER("target halted in %s state due to %s, current mode: %s\n"
@@ -758,8 +762,8 @@ int arm_arch_state(struct target *target)
arm_mode_name(arm->core_mode),
buf_get_u32(arm->cpsr->value, 0, 32),
buf_get_u32(arm->pc->value, 0, 32),
arm->is_semihosting ? ", semihosting" : "",
arm->is_semihosting_fileio ? " fileio" : "");
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
(target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
return ERROR_OK;
}
@@ -1094,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
return JIM_OK;
}
COMMAND_HANDLER(handle_arm_semihosting_command)
{
struct target *target = get_current_target(CMD_CTX);
if (target == NULL) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
struct arm *arm = target_to_arm(target);
if (!is_arm(arm)) {
command_print(CMD_CTX, "current target isn't an ARM");
return ERROR_FAIL;
}
if (!arm->setup_semihosting) {
command_print(CMD_CTX, "semihosting not supported for current target");
return ERROR_FAIL;
}
if (CMD_ARGC > 0) {
int semihosting;
COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting);
if (!target_was_examined(target)) {
LOG_ERROR("Target not examined yet");
return ERROR_FAIL;
}
if (arm->setup_semihosting(target, semihosting) != ERROR_OK) {
LOG_ERROR("Failed to Configure semihosting");
return ERROR_FAIL;
}
/* FIXME never let that "catch" be dropped! */
arm->is_semihosting = semihosting;
}
command_print(CMD_CTX, "semihosting is %s",
arm->is_semihosting
? "enabled" : "disabled");
return ERROR_OK;
}
COMMAND_HANDLER(handle_arm_semihosting_fileio_command)
{
struct target *target = get_current_target(CMD_CTX);
if (target == NULL) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
struct arm *arm = target_to_arm(target);
if (!is_arm(arm)) {
command_print(CMD_CTX, "current target isn't an ARM");
return ERROR_FAIL;
}
if (!arm->is_semihosting) {
command_print(CMD_CTX, "semihosting is not enabled");
return ERROR_FAIL;
}
if (CMD_ARGC > 0)
COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio);
command_print(CMD_CTX, "semihosting fileio is %s",
arm->is_semihosting_fileio
? "enabled" : "disabled");
return ERROR_OK;
}
COMMAND_HANDLER(handle_arm_semihosting_cmdline)
{
struct target *target = get_current_target(CMD_CTX);
unsigned int i;
if (target == NULL) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
struct arm *arm = target_to_arm(target);
if (!is_arm(arm)) {
command_print(CMD_CTX, "current target isn't an ARM");
return ERROR_FAIL;
}
if (!arm->setup_semihosting) {
command_print(CMD_CTX, "semihosting not supported for current target");
return ERROR_FAIL;
}
free(arm->semihosting_cmdline);
arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
for (i = 1; i < CMD_ARGC; i++) {
char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]);
if (cmdline == NULL)
break;
free(arm->semihosting_cmdline);
arm->semihosting_cmdline = cmdline;
}
return ERROR_OK;
}
extern __COMMAND_HANDLER(handle_common_semihosting_command);
extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
static const struct command_registration arm_exec_command_handlers[] = {
{
@@ -1245,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = {
},
{
"semihosting",
.handler = handle_arm_semihosting_command,
.handler = handle_common_semihosting_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting operations",
},
{
"semihosting_cmdline",
.handler = handle_arm_semihosting_cmdline,
.handler = handle_common_semihosting_cmdline,
.mode = COMMAND_EXEC,
.usage = "arguments",
.help = "command line arguments to be passed to program",
},
{
"semihosting_fileio",
.handler = handle_arm_semihosting_fileio_command,
.handler = handle_common_semihosting_fileio_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting fileio operations",
},
{
"semihosting_resexit",
.handler = handle_common_semihosting_resumable_exit_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting resumable exit",
},
COMMAND_REGISTRATION_DONE
};
const struct command_registration arm_command_handlers[] = {
+1 -6
View File
@@ -124,7 +124,7 @@ done:
return retval;
}
static int armv7a_read_ttbcr(struct target *target)
int armv7a_read_ttbcr(struct target *target)
{
struct armv7a_common *armv7a = target_to_armv7a(target);
struct arm_dpm *dpm = armv7a->arm.dpm;
@@ -554,9 +554,6 @@ int armv7a_identify_cache(struct target *target)
struct armv7a_cache_common *cache =
&(armv7a->armv7a_mmu.armv7a_cache);
if (!armv7a->is_armv7r)
armv7a_read_ttbcr(target);
retval = dpm->prepare(dpm);
if (retval != ERROR_OK)
goto done;
@@ -729,8 +726,6 @@ int armv7a_arch_state(struct target *target)
arm_arch_state(target);
armv7a_read_ttbcr(target);
if (armv7a->is_armv7r) {
LOG_USER("D-Cache: %s, I-Cache: %s",
state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
+1
View File
@@ -194,6 +194,7 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val);
int armv7a_handle_cache_info_command(struct command_context *cmd_ctx,
struct armv7a_cache_common *armv7a_cache);
int armv7a_read_ttbcr(struct target *target);
extern const struct command_registration armv7a_command_handlers[];
+23 -4
View File
@@ -70,6 +70,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
LOG_DEBUG("cl %" PRId32, cl);
do {
keep_alive();
c_way = size->way;
do {
uint32_t value = (c_index << size->index_shift)
@@ -89,6 +90,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
} while (c_index >= 0);
done:
keep_alive();
return retval;
}
@@ -164,7 +166,7 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
int retval;
int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -198,6 +200,8 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
}
while (va_line < va_end) {
if ((i++ & 0x3f) == 0)
keep_alive();
/* DCIMVAC - Invalidate data cache line by VA to PoC. */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
@@ -206,11 +210,13 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
keep_alive();
dpm->finish(dpm);
return retval;
@@ -224,7 +230,7 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
int retval;
int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -238,6 +244,8 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
if ((i++ & 0x3f) == 0)
keep_alive();
/* DCCMVAC - Data Cache Clean by MVA to PoC */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
@@ -246,11 +254,13 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
keep_alive();
dpm->finish(dpm);
return retval;
@@ -264,7 +274,7 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->dminline;
uint32_t va_line, va_end;
int retval;
int retval, i = 0;
retval = armv7a_l1_d_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -278,6 +288,8 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
if ((i++ & 0x3f) == 0)
keep_alive();
/* DCCIMVAC */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
@@ -286,11 +298,13 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
va_line += linelen;
}
keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("d-cache invalidate failed");
keep_alive();
dpm->finish(dpm);
return retval;
@@ -342,7 +356,7 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
&armv7a->armv7a_mmu.armv7a_cache;
uint32_t linelen = armv7a_cache->iminline;
uint32_t va_line, va_end;
int retval;
int retval, i = 0;
retval = armv7a_l1_i_cache_sanity_check(target);
if (retval != ERROR_OK)
@@ -356,6 +370,8 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
va_end = virt + size;
while (va_line < va_end) {
if ((i++ & 0x3f) == 0)
keep_alive();
/* ICIMVAU - Invalidate instruction cache by VA to PoU. */
retval = dpm->instr_write_data_r0(dpm,
ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
@@ -368,10 +384,13 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
goto done;
va_line += linelen;
}
keep_alive();
dpm->finish(dpm);
return retval;
done:
LOG_ERROR("i-cache invalidate failed");
keep_alive();
dpm->finish(dpm);
return retval;
+7 -3
View File
@@ -11,6 +11,9 @@
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* Copyright (C) 2018 by Liviu Ionescu *
* <ilg@livius.net> *
* *
* 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 2 of the License, or *
@@ -37,6 +40,7 @@
#include "armv7m.h"
#include "algorithm.h"
#include "register.h"
#include "semihosting_common.h"
#if 0
#define _DEBUG_INSTRUCTION_EXECUTION_
@@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target)
uint32_t ctrl, sp;
/* avoid filling log waiting for fileio reply */
if (arm->semihosting_hit_fileio)
if (target->semihosting && target->semihosting->hit_fileio)
return ERROR_OK;
ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
@@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target)
buf_get_u32(arm->pc->value, 0, 32),
(ctrl & 0x02) ? 'p' : 'm',
sp,
arm->is_semihosting ? ", semihosting" : "",
arm->is_semihosting_fileio ? " fileio" : "");
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
(target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
return ERROR_OK;
}
+10 -10
View File
@@ -62,7 +62,7 @@ int armv7m_trace_tpiu_config(struct target *target)
target_unregister_timer_callback(armv7m_poll_trace, target);
retval = adapter_config_trace(trace_config->config_type == INTERNAL,
retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
trace_config->pin_protocol,
trace_config->port_size,
&trace_config->trace_freq);
@@ -83,7 +83,7 @@ int armv7m_trace_tpiu_config(struct target *target)
trace_config->trace_freq, trace_config->traceclkin_freq,
trace_freq);
trace_config->trace_freq = trace_freq;
retval = adapter_config_trace(trace_config->config_type == INTERNAL,
retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
trace_config->pin_protocol,
trace_config->port_size,
&trace_config->trace_freq);
@@ -115,7 +115,7 @@ int armv7m_trace_tpiu_config(struct target *target)
if (retval != ERROR_OK)
return retval;
if (trace_config->config_type == INTERNAL)
if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL)
target_register_timer_callback(armv7m_poll_trace, 1, 1, target);
target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
@@ -173,7 +173,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
if (CMD_ARGC == cmd_idx + 1) {
close_trace_file(armv7m);
armv7m->trace_config.config_type = DISABLED;
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
if (CMD_CTX->mode == COMMAND_EXEC)
return armv7m_trace_tpiu_config(target);
else
@@ -183,13 +183,13 @@ COMMAND_HANDLER(handle_tpiu_config_command)
!strcmp(CMD_ARGV[cmd_idx], "internal")) {
close_trace_file(armv7m);
armv7m->trace_config.config_type = EXTERNAL;
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
cmd_idx++;
if (CMD_ARGC == cmd_idx)
return ERROR_COMMAND_SYNTAX_ERROR;
armv7m->trace_config.config_type = INTERNAL;
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
@@ -204,7 +204,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
return ERROR_COMMAND_SYNTAX_ERROR;
if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
armv7m->trace_config.pin_protocol = SYNC;
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC;
cmd_idx++;
if (CMD_ARGC == cmd_idx)
@@ -213,9 +213,9 @@ COMMAND_HANDLER(handle_tpiu_config_command)
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
} else {
if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER;
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER;
else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
armv7m->trace_config.pin_protocol = ASYNC_UART;
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART;
else
return ERROR_COMMAND_SYNTAX_ERROR;
@@ -237,7 +237,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
cmd_idx++;
} else {
if (armv7m->trace_config.config_type != INTERNAL) {
if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) {
LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
return ERROR_COMMAND_SYNTAX_ERROR;
}
+8 -8
View File
@@ -27,15 +27,15 @@
*/
enum trace_config_type {
DISABLED, /**< tracing is disabled */
EXTERNAL, /**< trace output is captured externally */
INTERNAL /**< trace output is handled by OpenOCD adapter driver */
TRACE_CONFIG_TYPE_DISABLED, /**< tracing is disabled */
TRACE_CONFIG_TYPE_EXTERNAL, /**< trace output is captured externally */
TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */
};
enum tpio_pin_protocol {
SYNC, /**< synchronous trace output */
ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
ASYNC_UART /**< asynchronous output with NRZ coding */
enum tpiu_pin_protocol {
TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */
TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */
};
enum itm_ts_prescaler {
@@ -50,7 +50,7 @@ struct armv7m_trace_config {
enum trace_config_type config_type;
/** Currently active trace output mode */
enum tpio_pin_protocol pin_protocol;
enum tpiu_pin_protocol pin_protocol;
/** TPIU formatter enable/disable (in async mode) */
bool formatter;
/** Synchronous output port width */
+18 -1
View File
@@ -1,6 +1,9 @@
/***************************************************************************
* Copyright (C) 2015 by David Ung *
* *
* Copyright (C) 2018 by Liviu Ionescu *
* <ilg@livius.net> *
* *
* 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 2 of the License, or *
@@ -36,6 +39,7 @@
#include "armv8_opcodes.h"
#include "target.h"
#include "target_type.h"
#include "semihosting_common.h"
static const char * const armv8_state_strings[] = {
"AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64",
@@ -1013,11 +1017,24 @@ int armv8_handle_cache_info_command(struct command_context *cmd_ctx,
return ERROR_OK;
}
static int armv8_setup_semihosting(struct target *target, int enable)
{
struct arm *arm = target_to_arm(target);
if (arm->core_state != ARM_STATE_AARCH64) {
LOG_ERROR("semihosting only supported in AArch64 state\n");
return ERROR_FAIL;
}
return ERROR_OK;
}
int armv8_init_arch_info(struct target *target, struct armv8_common *armv8)
{
struct arm *arm = &armv8->arm;
arm->arch_info = armv8;
target->arch_info = &armv8->arm;
arm->setup_semihosting = armv8_setup_semihosting;
/* target is useful in all function arm v4 5 compatible */
armv8->arm.target = target;
armv8->arm.common_magic = ARM_COMMON_MAGIC;
@@ -1046,7 +1063,7 @@ int armv8_aarch64_state(struct target *target)
armv8_mode_name(arm->core_mode),
buf_get_u32(arm->cpsr->value, 0, 32),
buf_get_u64(arm->pc->value, 0, 64),
arm->is_semihosting ? ", semihosting" : "");
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "");
return ERROR_OK;
}
+14
View File
@@ -113,6 +113,12 @@ enum {
ARMV8_LAST_REG,
};
enum run_control_op {
ARMV8_RUNCONTROL_UNKNOWN = 0,
ARMV8_RUNCONTROL_RESUME = 1,
ARMV8_RUNCONTROL_HALT = 2,
ARMV8_RUNCONTROL_STEP = 3,
};
#define ARMV8_COMMON_MAGIC 0x0A450AAA
@@ -210,6 +216,9 @@ struct armv8_common {
struct arm_cti *cti;
/* last run-control command issued to this target (resume, halt, step) */
enum run_control_op last_run_control_op;
/* Direct processor core register read and writes */
int (*read_reg_u64)(struct armv8_common *armv8, int num, uint64_t *value);
int (*write_reg_u64)(struct armv8_common *armv8, int num, uint64_t value);
@@ -232,6 +241,11 @@ target_to_armv8(struct target *target)
return container_of(target->arch_info, struct armv8_common, arm);
}
static inline bool is_armv8(struct armv8_common *armv8)
{
return armv8->common_magic == ARMV8_COMMON_MAGIC;
}
/* register offsets from armv8.debug_base */
#define CPUV8_DBG_MAINID0 0xD00
#define CPUV8_DBG_CPUFEATURE0 0xD20
+2 -5
View File
@@ -315,11 +315,8 @@ int breakpoint_remove_internal(struct target *target, target_addr_t address)
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
if ((breakpoint->address == address) && (breakpoint->asid == 0))
break;
else if ((breakpoint->address == 0) && (breakpoint->asid == address))
break;
else if ((breakpoint->address == address) && (breakpoint->asid != 0))
if ((breakpoint->address == address) ||
(breakpoint->address == 0 && breakpoint->asid == address))
break;
breakpoint = breakpoint->next;
}
+18 -1
View File
@@ -1297,6 +1297,9 @@ static int cortex_a_post_debug_entry(struct target *target)
LOG_DEBUG("cp15_control_reg: %8.8" PRIx32, cortex_a->cp15_control_reg);
cortex_a->cp15_control_reg_curr = cortex_a->cp15_control_reg;
if (!armv7a->is_armv7r)
armv7a_read_ttbcr(target);
if (armv7a->armv7a_mmu.armv7a_cache.info == -1)
armv7a_identify_cache(target);
@@ -3226,6 +3229,20 @@ static int cortex_a_virt2phys(struct target *target,
struct armv7a_common *armv7a = target_to_armv7a(target);
struct adiv5_dap *swjdp = armv7a->arm.dap;
uint8_t apsel = swjdp->apsel;
int mmu_enabled = 0;
/*
* If the MMU was not enabled at debug entry, there is no
* way of knowing if there was ever a valid configuration
* for it and thus it's not safe to enable it. In this case,
* just return the virtual address as physical.
*/
cortex_a_mmu(target, &mmu_enabled);
if (!mmu_enabled) {
*phys = virt;
return ERROR_OK;
}
if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) {
uint32_t ret;
retval = armv7a_mmu_translate_va(target,
@@ -3421,7 +3438,7 @@ static const struct command_registration cortex_a_exec_command_handlers[] = {
{
.name = "dacrfixup",
.handler = handle_cortex_a_dacrfixup_command,
.mode = COMMAND_EXEC,
.mode = COMMAND_ANY,
.help = "set domain access control (DACR) to all-manager "
"on memory access",
.usage = "['on'|'off']",
+29 -22
View File
@@ -165,7 +165,7 @@ static int cortex_m_single_step_core(struct target *target)
struct armv7m_common *armv7m = &cortex_m->armv7m;
int retval;
/* Mask interrupts before clearing halt, if done already. This avoids
/* Mask interrupts before clearing halt, if not done already. This avoids
* Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing
* HALT can put the core into an unknown state.
*/
@@ -237,8 +237,11 @@ static int cortex_m_endreset_event(struct target *target)
return retval;
}
/* clear any interrupt masking */
cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
/* Restore proper interrupt masking setting. */
if (cortex_m->isrmasking_mode == CORTEX_M_ISRMASK_ON)
cortex_m_write_debug_halt_mask(target, C_MASKINTS, 0);
else
cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
/* Enable features controlled by ITM and DWT blocks, and catch only
* the vectors we were told to pay attention to.
@@ -1137,6 +1140,10 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint
breakpoint->set = fp_num + 1;
fpcr_value = breakpoint->address | 1;
if (cortex_m->fp_rev == 0) {
if (breakpoint->address > 0x1FFFFFFF) {
LOG_ERROR("Cortex-M Flash Patch Breakpoint rev.1 cannot handle HW breakpoint above address 0x1FFFFFFE");
return ERROR_FAIL;
}
uint32_t hilo;
hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW;
fpcr_value = (fpcr_value & 0x1FFFFFFC) | hilo | 1;
@@ -1806,11 +1813,11 @@ static int cortex_m_dwt_set_reg(struct reg *reg, uint8_t *buf)
struct dwt_reg {
uint32_t addr;
char *name;
const char *name;
unsigned size;
};
static struct dwt_reg dwt_base_regs[] = {
static const struct dwt_reg dwt_base_regs[] = {
{ DWT_CTRL, "dwt_ctrl", 32, },
/* NOTE that Erratum 532314 (fixed r2p0) affects CYCCNT: it wrongly
* increments while the core is asleep.
@@ -1819,7 +1826,7 @@ static struct dwt_reg dwt_base_regs[] = {
/* plus some 8 bit counters, useful for profiling with TPIU */
};
static struct dwt_reg dwt_comp[] = {
static const struct dwt_reg dwt_comp[] = {
#define DWT_COMPARATOR(i) \
{ DWT_COMP0 + 0x10 * (i), "dwt_" #i "_comp", 32, }, \
{ DWT_MASK0 + 0x10 * (i), "dwt_" #i "_mask", 4, }, \
@@ -1848,7 +1855,7 @@ static const struct reg_arch_type dwt_reg_type = {
.set = cortex_m_dwt_set_reg,
};
static void cortex_m_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d)
static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dwt_reg *d)
{
struct dwt_reg_state *state;
@@ -2076,7 +2083,7 @@ int cortex_m_examine(struct target *target)
if (retval != ERROR_OK)
return retval;
if (armv7m->trace_config.config_type != DISABLED) {
if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_DISABLED) {
armv7m_trace_tpiu_config(target);
armv7m_trace_itm_config(target);
}
@@ -2270,20 +2277,6 @@ static int cortex_m_verify_pointer(struct command_context *cmd_ctx,
* cortexm3_target structure, which is only used with CM3 targets.
*/
static const struct {
char name[10];
unsigned mask;
} vec_ids[] = {
{ "hard_err", VC_HARDERR, },
{ "int_err", VC_INTERR, },
{ "bus_err", VC_BUSERR, },
{ "state_err", VC_STATERR, },
{ "chk_err", VC_CHKERR, },
{ "nocp_err", VC_NOCPERR, },
{ "mm_err", VC_MMERR, },
{ "reset", VC_CORERESET, },
};
COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
{
struct target *target = get_current_target(CMD_CTX);
@@ -2292,6 +2285,20 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
uint32_t demcr = 0;
int retval;
static const struct {
char name[10];
unsigned mask;
} vec_ids[] = {
{ "hard_err", VC_HARDERR, },
{ "int_err", VC_INTERR, },
{ "bus_err", VC_BUSERR, },
{ "state_err", VC_STATERR, },
{ "chk_err", VC_CHKERR, },
{ "nocp_err", VC_NOCPERR, },
{ "mm_err", VC_MMERR, },
{ "reset", VC_CORERESET, },
};
retval = cortex_m_verify_pointer(CMD_CTX, cortex_m);
if (retval != ERROR_OK)
return retval;
+1 -2
View File
@@ -1048,8 +1048,7 @@ int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksu
static bool first_init;
if (!first_init) {
/* Initialize the CRC table and the decoding table. */
int i, j;
unsigned int c;
unsigned int i, j, c;
for (i = 0; i < 256; i++) {
/* as per gdb */
for (c = i << 24, j = 8; j > 0; --j)
+2
View File
@@ -344,6 +344,8 @@ static int mips_m4k_assert_reset(struct target *target)
jtag_add_reset(1, 1);
else if (!srst_asserted)
jtag_add_reset(0, 1);
} else if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
} else {
if (mips_m4k->is_pic32mx) {
LOG_DEBUG("Using MTAP reset to reset processor...");
+36 -37
View File
@@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
fileio_info->identifier = NULL;
}
uint32_t reg_r0, reg_r1, reg_r2;
nds32_get_mapped_reg(nds32, R0, &reg_r0);
nds32_get_mapped_reg(nds32, R1, &reg_r1);
nds32_get_mapped_reg(nds32, R2, &reg_r2);
switch (syscall_id) {
case NDS32_SYSCALL_EXIT:
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "exit");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_OPEN:
{
uint8_t filename[256];
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "open");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of path */
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4));
fileio_info->param_3 = reg_r1;
fileio_info->param_4 = reg_r2;
target->type->read_buffer(target, fileio_info->param_1,
256, filename);
target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_CLOSE:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "close");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_READ:
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "read");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
fileio_info->param_1 = reg_r0;
fileio_info->param_2 = reg_r1;
fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_WRITE:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "write");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
fileio_info->param_1 = reg_r0;
fileio_info->param_2 = reg_r1;
fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_LSEEK:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "lseek");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
fileio_info->param_1 = reg_r0;
fileio_info->param_2 = reg_r1;
fileio_info->param_3 = reg_r2;
break;
case NDS32_SYSCALL_UNLINK:
{
uint8_t filename[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "unlink");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of path */
target->type->read_buffer(target, fileio_info->param_1,
256, filename);
target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
@@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
uint8_t filename[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "rename");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
fileio_info->param_3 = reg_r1;
/* reserve fileio_info->param_4 for length of new path */
target->type->read_buffer(target, fileio_info->param_1,
256, filename);
target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
target->type->read_buffer(target, fileio_info->param_3,
256, filename);
target->type->read_buffer(target, reg_r1, 256, filename);
fileio_info->param_4 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_FSTAT:
fileio_info->identifier = malloc(6);
sprintf(fileio_info->identifier, "fstat");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
fileio_info->param_1 = reg_r0;
fileio_info->param_2 = reg_r1;
break;
case NDS32_SYSCALL_STAT:
{
uint8_t filename[256];
fileio_info->identifier = malloc(5);
sprintf(fileio_info->identifier, "stat");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
fileio_info->param_3 = reg_r1;
target->type->read_buffer(target, fileio_info->param_1,
256, filename);
target->type->read_buffer(target, reg_r0, 256, filename);
fileio_info->param_2 = strlen((char *)filename) + 1;
}
break;
case NDS32_SYSCALL_GETTIMEOFDAY:
fileio_info->identifier = malloc(13);
sprintf(fileio_info->identifier, "gettimeofday");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
fileio_info->param_1 = reg_r0;
fileio_info->param_2 = reg_r1;
break;
case NDS32_SYSCALL_ISATTY:
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "isatty");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
break;
case NDS32_SYSCALL_SYSTEM:
{
uint8_t command[256];
fileio_info->identifier = malloc(7);
sprintf(fileio_info->identifier, "system");
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
fileio_info->param_1 = reg_r0;
/* reserve fileio_info->param_2 for length of old path */
target->type->read_buffer(target, fileio_info->param_1,
256, command);
target->type->read_buffer(target, reg_r0, 256, command);
fileio_info->param_2 = strlen((char *)command) + 1;
}
break;
+16
View File
@@ -0,0 +1,16 @@
noinst_LTLIBRARIES += %D%/libriscv.la
%C%_libriscv_la_SOURCES = \
%D%/asm.h \
%D%/batch.h \
%D%/debug_defines.h \
%D%/encoding.h \
%D%/gdb_regs.h \
%D%/opcodes.h \
%D%/program.h \
%D%/riscv.h \
%D%/batch.c \
%D%/program.c \
%D%/riscv-011.c \
%D%/riscv-013.c \
%D%/riscv.c \
%D%/riscv_semihosting.c
+220 -164
View File
@@ -229,7 +229,8 @@
* Explains why Debug Mode was entered.
*
* When there are multiple reasons to enter Debug Mode in a single
* cycle, the cause with the highest priority is the one written.
* cycle, hardware should set \Fcause to the cause with the highest
* priority.
*
* 1: An {\tt ebreak} instruction was executed. (priority 3)
*
@@ -245,6 +246,25 @@
#define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET)
/*
* When 1, \Fmprv in \Rmstatus takes effect during debug mode.
* When 0, it is ignored during debug mode.
* Implementing this bit is optional.
* If not implemented it should be tied to 0.
*/
#define CSR_DCSR_MPRVEN_OFFSET 4
#define CSR_DCSR_MPRVEN_LENGTH 1
#define CSR_DCSR_MPRVEN (0x1U << CSR_DCSR_MPRVEN_OFFSET)
/*
* When set, there is a Non-Maskable-Interrupt (NMI) pending for the hart.
*
* Since an NMI can indicate a hardware error condition,
* reliable debugging may no longer be possible once this bit becomes set.
* This is implementation-dependent.
*/
#define CSR_DCSR_NMIP_OFFSET 3
#define CSR_DCSR_NMIP_LENGTH 1
#define CSR_DCSR_NMIP (0x1U << CSR_DCSR_NMIP_OFFSET)
/*
* When set and not in Debug Mode, the hart will only execute a single
* instruction and then enter Debug Mode.
* If the instruction does not complete due to an exception,
@@ -269,14 +289,14 @@
#define CSR_DCSR_PRV (0x3U << CSR_DCSR_PRV_OFFSET)
#define CSR_DPC 0x7b1
#define CSR_DPC_DPC_OFFSET 0
#define CSR_DPC_DPC_LENGTH XLEN
#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET)
#define CSR_DPC_DPC_LENGTH MXLEN
#define CSR_DPC_DPC (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET)
#define CSR_DSCRATCH0 0x7b2
#define CSR_DSCRATCH1 0x7b3
#define CSR_TSELECT 0x7a0
#define CSR_TSELECT_INDEX_OFFSET 0
#define CSR_TSELECT_INDEX_LENGTH XLEN
#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
#define CSR_TSELECT_INDEX_LENGTH MXLEN
#define CSR_TSELECT_INDEX (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
#define CSR_TDATA1 0x7a1
/*
* 0: There is no trigger at this \Rtselect.
@@ -290,12 +310,22 @@
* 3: The trigger is an instruction count trigger. The remaining bits
* in this register act as described in \Ricount.
*
* 4: The trigger is an interrupt trigger. The remaining bits
* in this register act as described in \Ritrigger.
*
* 5: The trigger is an exception trigger. The remaining bits
* in this register act as described in \Retrigger.
*
* 15: This trigger exists (so enumeration shouldn't terminate), but
* is not currently available.
*
* Other values are reserved for future use.
*
* When this field is written to an unsupported value, it takes on its
* reset value instead. The reset value is any one of the types
* supported by the trigger selected by \Rtselect.
*/
#define CSR_TDATA1_TYPE_OFFSET (XLEN-4)
#define CSR_TDATA1_TYPE_OFFSET (MXLEN-4)
#define CSR_TDATA1_TYPE_LENGTH 4
#define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET)
/*
@@ -307,39 +337,57 @@
*
* This bit is only writable from Debug Mode.
*/
#define CSR_TDATA1_DMODE_OFFSET (XLEN-5)
#define CSR_TDATA1_DMODE_OFFSET (MXLEN-5)
#define CSR_TDATA1_DMODE_LENGTH 1
#define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET)
/*
* Trigger-specific data.
*/
#define CSR_TDATA1_DATA_OFFSET 0
#define CSR_TDATA1_DATA_LENGTH (XLEN - 5)
#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
#define CSR_TDATA1_DATA_LENGTH (MXLEN - 5)
#define CSR_TDATA1_DATA (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
#define CSR_TDATA2 0x7a2
#define CSR_TDATA2_DATA_OFFSET 0
#define CSR_TDATA2_DATA_LENGTH XLEN
#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
#define CSR_TDATA2_DATA_LENGTH MXLEN
#define CSR_TDATA2_DATA (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET)
#define CSR_TDATA3 0x7a3
#define CSR_TDATA3_DATA_OFFSET 0
#define CSR_TDATA3_DATA_LENGTH XLEN
#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
#define CSR_TDATA3_DATA_LENGTH MXLEN
#define CSR_TDATA3_DATA (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET)
#define CSR_TINFO 0x7a4
/*
* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N
* corresponds to type N. If the bit is set, then that type is
* supported by the currently selected trigger.
*
* If the currently selected trigger doesn't exist, this field
* contains 1.
*
* If \Ftype is not writable, this register may be unimplemented, in
* which case reading it causes an illegal instruction exception. In
* this case the debugger can read the only supported type from
* \Rtdataone.
*/
#define CSR_TINFO_INFO_OFFSET 0
#define CSR_TINFO_INFO_LENGTH 16
#define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET)
#define CSR_MCONTROL 0x7a1
#define CSR_MCONTROL_TYPE_OFFSET (XLEN-4)
#define CSR_MCONTROL_TYPE_OFFSET (MXLEN-4)
#define CSR_MCONTROL_TYPE_LENGTH 4
#define CSR_MCONTROL_TYPE (0xfULL << CSR_MCONTROL_TYPE_OFFSET)
#define CSR_MCONTROL_DMODE_OFFSET (XLEN-5)
#define CSR_MCONTROL_DMODE_OFFSET (MXLEN-5)
#define CSR_MCONTROL_DMODE_LENGTH 1
#define CSR_MCONTROL_DMODE (0x1ULL << CSR_MCONTROL_DMODE_OFFSET)
/*
* Specifies the largest naturally aligned powers-of-two (NAPOT) range
* supported by the hardware. The value is the logarithm base 2 of the
* supported by the hardware when \Fmatch is 1. The value is the
* logarithm base 2 of the
* number of bytes in that range. A value of 0 indicates that only
* exact value matches are supported (one byte range). A value of 63
* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
* size.
*/
#define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11)
#define CSR_MCONTROL_MASKMAX_OFFSET (MXLEN-11)
#define CSR_MCONTROL_MASKMAX_LENGTH 6
#define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
/*
@@ -390,22 +438,8 @@
#define CSR_MCONTROL_TIMING_LENGTH 1
#define CSR_MCONTROL_TIMING (0x1ULL << CSR_MCONTROL_TIMING_OFFSET)
/*
* Determines what happens when this trigger matches.
*
* 0: Raise a breakpoint exception. (Used when software wants to use
* the trigger module without an external debugger attached.)
*
* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
*
* 2: Start tracing.
*
* 3: Stop tracing.
*
* 4: Emit trace data for this match. If it is a data access match,
* emit appropriate Load/Store Address/Data. If it is an instruction
* execution, emit its PC.
*
* Other values are reserved for future use.
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
#define CSR_MCONTROL_ACTION_OFFSET 12
#define CSR_MCONTROL_ACTION_LENGTH 6
@@ -415,6 +449,18 @@
*
* 1: While this trigger does not match, it prevents the trigger with
* the next index from matching.
*
* Because \Fchain affects the next trigger, hardware must zero it in
* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has
* \Fdmode of 1.
* In addition hardware should ignore writes to \Rmcontrol that set
* \Fdmode to 1 if the previous trigger has both \Fdmode of 0 and
* \Fchain of 1. Debuggers must avoid the latter case by checking
* \Fchain on the previous trigger if they're writing \Rmcontrol.
*
* Implementations that wish to limit the maximum length of a trigger
* chain (eg. to meet timing requirements) may do so by zeroing
* \Fchain in writes to \Rmcontrol that would make the chain too long.
*/
#define CSR_MCONTROL_CHAIN_OFFSET 11
#define CSR_MCONTROL_CHAIN_LENGTH 1
@@ -423,7 +469,7 @@
* 0: Matches when the value equals \Rtdatatwo.
*
* 1: Matches when the top M bits of the value match the top M bits of
* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant
* bit containing 0 in \Rtdatatwo.
*
* 2: Matches when the value is greater than (unsigned) or equal to
@@ -482,10 +528,10 @@
#define CSR_MCONTROL_LOAD_LENGTH 1
#define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
#define CSR_ICOUNT 0x7a1
#define CSR_ICOUNT_TYPE_OFFSET (XLEN-4)
#define CSR_ICOUNT_TYPE_OFFSET (MXLEN-4)
#define CSR_ICOUNT_TYPE_LENGTH 4
#define CSR_ICOUNT_TYPE (0xfULL << CSR_ICOUNT_TYPE_OFFSET)
#define CSR_ICOUNT_DMODE_OFFSET (XLEN-5)
#define CSR_ICOUNT_DMODE_OFFSET (MXLEN-5)
#define CSR_ICOUNT_DMODE_LENGTH 1
#define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
/*
@@ -529,26 +575,102 @@
#define CSR_ICOUNT_U_LENGTH 1
#define CSR_ICOUNT_U (0x1ULL << CSR_ICOUNT_U_OFFSET)
/*
* Determines what happens when this trigger matches.
*
* 0: Raise a breakpoint exception. (Used when software wants to use the
* trigger module without an external debugger attached.)
*
* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
*
* 2: Start tracing.
*
* 3: Stop tracing.
*
* 4: Emit trace data for this match. If it is a data access match,
* emit appropriate Load/Store Address/Data. If it is an instruction
* execution, emit its PC.
*
* Other values are reserved for future use.
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
#define CSR_ICOUNT_ACTION_OFFSET 0
#define CSR_ICOUNT_ACTION_LENGTH 6
#define CSR_ICOUNT_ACTION (0x3fULL << CSR_ICOUNT_ACTION_OFFSET)
#define CSR_ITRIGGER 0x7a1
#define CSR_ITRIGGER_TYPE_OFFSET (MXLEN-4)
#define CSR_ITRIGGER_TYPE_LENGTH 4
#define CSR_ITRIGGER_TYPE (0xfULL << CSR_ITRIGGER_TYPE_OFFSET)
#define CSR_ITRIGGER_DMODE_OFFSET (MXLEN-5)
#define CSR_ITRIGGER_DMODE_LENGTH 1
#define CSR_ITRIGGER_DMODE (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET)
/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
* time. The trigger's user can use this bit to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
#define CSR_ITRIGGER_HIT_OFFSET (MXLEN-6)
#define CSR_ITRIGGER_HIT_LENGTH 1
#define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
/*
* When set, enable this trigger for interrupts that are taken from M
* mode.
*/
#define CSR_ITRIGGER_M_OFFSET 9
#define CSR_ITRIGGER_M_LENGTH 1
#define CSR_ITRIGGER_M (0x1ULL << CSR_ITRIGGER_M_OFFSET)
/*
* When set, enable this trigger for interrupts that are taken from S
* mode.
*/
#define CSR_ITRIGGER_S_OFFSET 7
#define CSR_ITRIGGER_S_LENGTH 1
#define CSR_ITRIGGER_S (0x1ULL << CSR_ITRIGGER_S_OFFSET)
/*
* When set, enable this trigger for interrupts that are taken from U
* mode.
*/
#define CSR_ITRIGGER_U_OFFSET 6
#define CSR_ITRIGGER_U_LENGTH 1
#define CSR_ITRIGGER_U (0x1ULL << CSR_ITRIGGER_U_OFFSET)
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
#define CSR_ITRIGGER_ACTION_OFFSET 0
#define CSR_ITRIGGER_ACTION_LENGTH 6
#define CSR_ITRIGGER_ACTION (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET)
#define CSR_ETRIGGER 0x7a1
#define CSR_ETRIGGER_TYPE_OFFSET (MXLEN-4)
#define CSR_ETRIGGER_TYPE_LENGTH 4
#define CSR_ETRIGGER_TYPE (0xfULL << CSR_ETRIGGER_TYPE_OFFSET)
#define CSR_ETRIGGER_DMODE_OFFSET (MXLEN-5)
#define CSR_ETRIGGER_DMODE_LENGTH 1
#define CSR_ETRIGGER_DMODE (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET)
/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
* time. The trigger's user can use this bit to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
#define CSR_ETRIGGER_HIT_OFFSET (MXLEN-6)
#define CSR_ETRIGGER_HIT_LENGTH 1
#define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
/*
* When set, enable this trigger for exceptions that are taken from M
* mode.
*/
#define CSR_ETRIGGER_M_OFFSET 9
#define CSR_ETRIGGER_M_LENGTH 1
#define CSR_ETRIGGER_M (0x1ULL << CSR_ETRIGGER_M_OFFSET)
/*
* When set, enable this trigger for exceptions that are taken from S
* mode.
*/
#define CSR_ETRIGGER_S_OFFSET 7
#define CSR_ETRIGGER_S_LENGTH 1
#define CSR_ETRIGGER_S (0x1ULL << CSR_ETRIGGER_S_OFFSET)
/*
* When set, enable this trigger for exceptions that are taken from U
* mode.
*/
#define CSR_ETRIGGER_U_OFFSET 6
#define CSR_ETRIGGER_U_LENGTH 1
#define CSR_ETRIGGER_U (0x1ULL << CSR_ETRIGGER_U_OFFSET)
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
#define CSR_ETRIGGER_ACTION_OFFSET 0
#define CSR_ETRIGGER_ACTION_LENGTH 6
#define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
#define DMI_DMSTATUS 0x11
/*
* If 1, then there is an implicit {\tt ebreak} instruction at the
@@ -657,6 +779,14 @@
#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1
#define DMI_DMSTATUS_AUTHBUSY (0x1U << DMI_DMSTATUS_AUTHBUSY_OFFSET)
/*
* 1 if this Debug Module supports halt-on-reset functionality
* controllable by the \Fsetresethaltreq and \Fclrresethaltreq bits.
* 0 otherwise.
*/
#define DMI_DMSTATUS_HASRESETHALTREQ_OFFSET 5
#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1
#define DMI_DMSTATUS_HASRESETHALTREQ (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET)
/*
* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
* is not relevant to the Device Tree.
*
@@ -762,6 +892,29 @@
#define DMI_DMCONTROL_HARTSELHI_LENGTH 10
#define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
/*
* This optional field writes the halt-on-reset request bit for all
* currently selected harts.
* When set to 1, each selected hart will halt upon the next deassertion
* of its reset. The halt-on-reset request bit is not automatically
* cleared. The debugger must write to \Fclrresethaltreq to clear it.
*
* Writes apply to the new value of \Fhartsel and \Fhasel.
*
* If \Fhasresethaltreq is 0, this field is not implemented.
*/
#define DMI_DMCONTROL_SETRESETHALTREQ_OFFSET 3
#define DMI_DMCONTROL_SETRESETHALTREQ_LENGTH 1
#define DMI_DMCONTROL_SETRESETHALTREQ (0x1U << DMI_DMCONTROL_SETRESETHALTREQ_OFFSET)
/*
* This optional field clears the halt-on-reset request bit for all
* currently selected harts.
*
* Writes apply to the new value of \Fhartsel and \Fhasel.
*/
#define DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET 2
#define DMI_DMCONTROL_CLRRESETHALTREQ_LENGTH 1
#define DMI_DMCONTROL_CLRRESETHALTREQ (0x1U << DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET)
/*
* This bit controls the reset signal from the DM to the rest of the
* system. The signal should reset every part of the system, including
* every hart, except for the DM and any logic required to access the
@@ -786,7 +939,7 @@
* Debug Module after power up, including the platform's system reset
* or Debug Transport reset signals.
*
* A debugger may pulse this bit low to get the debug module into a
* A debugger may pulse this bit low to get the Debug Module into a
* known state.
*
* Implementations may use this bit to aid debugging, for example by
@@ -808,7 +961,7 @@
#define DMI_HARTINFO_NSCRATCH (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET)
/*
* 0: The {\tt data} registers are shadowed in the hart by CSR
* registers. Each CSR register is XLEN bits in size, and corresponds
* registers. Each CSR register is MXLEN bits in size, and corresponds
* to a single argument, per Table~\ref{tab:datareg}.
*
* 1: The {\tt data} registers are shadowed in the hart's memory map.
@@ -999,7 +1152,7 @@
* it's explicitly cleared by the debugger.
*
* While this field is non-zero, no more system bus accesses can be
* initiated by the debug module.
* initiated by the Debug Module.
*/
#define DMI_SBCS_SBBUSYERROR_OFFSET 22
#define DMI_SBCS_SBBUSYERROR_LENGTH 1
@@ -1010,8 +1163,9 @@
* bit goes high immediately when a read or write is requested for any
* reason, and does not go low until the access is fully completed.
*
* To avoid race conditions, debuggers must not try to clear \Fsberror
* until they read \Fsbbusy as 0.
* Writes to \Rsbcs while \Fsbbusy is high result in undefined
* behavior. A debugger must not write to \Rsbcs until it reads
* \Fsbbusy as 0.
*/
#define DMI_SBCS_SBBUSY_OFFSET 21
#define DMI_SBCS_SBBUSY_LENGTH 1
@@ -1057,11 +1211,13 @@
#define DMI_SBCS_SBREADONDATA_LENGTH 1
#define DMI_SBCS_SBREADONDATA (0x1U << DMI_SBCS_SBREADONDATA_OFFSET)
/*
* When the debug module's system bus
* When the Debug Module's system bus
* master causes a bus error, this field gets set. The bits in this
* field remain set until they are cleared by writing 1 to them.
* While this field is non-zero, no more system bus accesses can be
* initiated by the debug module.
* initiated by the Debug Module.
*
* An implementation may report "Other" (7) for any error condition.
*
* 0: There was no bus error.
*
@@ -1069,7 +1225,11 @@
*
* 2: A bad address was accessed.
*
* 3: There was some other error (eg. alignment).
* 3: There was an alignment error.
*
* 4: An access of unsupported size was requested.
*
* 7: Other.
*/
#define DMI_SBCS_SBERROR_OFFSET 12
#define DMI_SBCS_SBERROR_LENGTH 3
@@ -1252,107 +1412,3 @@
#define VIRT_PRIV_PRV_OFFSET 0
#define VIRT_PRIV_PRV_LENGTH 2
#define VIRT_PRIV_PRV (0x3U << VIRT_PRIV_PRV_OFFSET)
#define DMI_SERCS 0x34
/*
* Number of supported serial ports.
*/
#define DMI_SERCS_SERIALCOUNT_OFFSET 28
#define DMI_SERCS_SERIALCOUNT_LENGTH 4
#define DMI_SERCS_SERIALCOUNT (0xfU << DMI_SERCS_SERIALCOUNT_OFFSET)
/*
* Select which serial port is accessed by \Rserrx and \Rsertx.
*/
#define DMI_SERCS_SERIAL_OFFSET 24
#define DMI_SERCS_SERIAL_LENGTH 3
#define DMI_SERCS_SERIAL (0x7U << DMI_SERCS_SERIAL_OFFSET)
#define DMI_SERCS_ERROR7_OFFSET 23
#define DMI_SERCS_ERROR7_LENGTH 1
#define DMI_SERCS_ERROR7 (0x1U << DMI_SERCS_ERROR7_OFFSET)
#define DMI_SERCS_VALID7_OFFSET 22
#define DMI_SERCS_VALID7_LENGTH 1
#define DMI_SERCS_VALID7 (0x1U << DMI_SERCS_VALID7_OFFSET)
#define DMI_SERCS_FULL7_OFFSET 21
#define DMI_SERCS_FULL7_LENGTH 1
#define DMI_SERCS_FULL7 (0x1U << DMI_SERCS_FULL7_OFFSET)
#define DMI_SERCS_ERROR6_OFFSET 20
#define DMI_SERCS_ERROR6_LENGTH 1
#define DMI_SERCS_ERROR6 (0x1U << DMI_SERCS_ERROR6_OFFSET)
#define DMI_SERCS_VALID6_OFFSET 19
#define DMI_SERCS_VALID6_LENGTH 1
#define DMI_SERCS_VALID6 (0x1U << DMI_SERCS_VALID6_OFFSET)
#define DMI_SERCS_FULL6_OFFSET 18
#define DMI_SERCS_FULL6_LENGTH 1
#define DMI_SERCS_FULL6 (0x1U << DMI_SERCS_FULL6_OFFSET)
#define DMI_SERCS_ERROR5_OFFSET 17
#define DMI_SERCS_ERROR5_LENGTH 1
#define DMI_SERCS_ERROR5 (0x1U << DMI_SERCS_ERROR5_OFFSET)
#define DMI_SERCS_VALID5_OFFSET 16
#define DMI_SERCS_VALID5_LENGTH 1
#define DMI_SERCS_VALID5 (0x1U << DMI_SERCS_VALID5_OFFSET)
#define DMI_SERCS_FULL5_OFFSET 15
#define DMI_SERCS_FULL5_LENGTH 1
#define DMI_SERCS_FULL5 (0x1U << DMI_SERCS_FULL5_OFFSET)
#define DMI_SERCS_ERROR4_OFFSET 14
#define DMI_SERCS_ERROR4_LENGTH 1
#define DMI_SERCS_ERROR4 (0x1U << DMI_SERCS_ERROR4_OFFSET)
#define DMI_SERCS_VALID4_OFFSET 13
#define DMI_SERCS_VALID4_LENGTH 1
#define DMI_SERCS_VALID4 (0x1U << DMI_SERCS_VALID4_OFFSET)
#define DMI_SERCS_FULL4_OFFSET 12
#define DMI_SERCS_FULL4_LENGTH 1
#define DMI_SERCS_FULL4 (0x1U << DMI_SERCS_FULL4_OFFSET)
#define DMI_SERCS_ERROR3_OFFSET 11
#define DMI_SERCS_ERROR3_LENGTH 1
#define DMI_SERCS_ERROR3 (0x1U << DMI_SERCS_ERROR3_OFFSET)
#define DMI_SERCS_VALID3_OFFSET 10
#define DMI_SERCS_VALID3_LENGTH 1
#define DMI_SERCS_VALID3 (0x1U << DMI_SERCS_VALID3_OFFSET)
#define DMI_SERCS_FULL3_OFFSET 9
#define DMI_SERCS_FULL3_LENGTH 1
#define DMI_SERCS_FULL3 (0x1U << DMI_SERCS_FULL3_OFFSET)
#define DMI_SERCS_ERROR2_OFFSET 8
#define DMI_SERCS_ERROR2_LENGTH 1
#define DMI_SERCS_ERROR2 (0x1U << DMI_SERCS_ERROR2_OFFSET)
#define DMI_SERCS_VALID2_OFFSET 7
#define DMI_SERCS_VALID2_LENGTH 1
#define DMI_SERCS_VALID2 (0x1U << DMI_SERCS_VALID2_OFFSET)
#define DMI_SERCS_FULL2_OFFSET 6
#define DMI_SERCS_FULL2_LENGTH 1
#define DMI_SERCS_FULL2 (0x1U << DMI_SERCS_FULL2_OFFSET)
#define DMI_SERCS_ERROR1_OFFSET 5
#define DMI_SERCS_ERROR1_LENGTH 1
#define DMI_SERCS_ERROR1 (0x1U << DMI_SERCS_ERROR1_OFFSET)
#define DMI_SERCS_VALID1_OFFSET 4
#define DMI_SERCS_VALID1_LENGTH 1
#define DMI_SERCS_VALID1 (0x1U << DMI_SERCS_VALID1_OFFSET)
#define DMI_SERCS_FULL1_OFFSET 3
#define DMI_SERCS_FULL1_LENGTH 1
#define DMI_SERCS_FULL1 (0x1U << DMI_SERCS_FULL1_OFFSET)
/*
* 1 when the debugger-to-core queue for serial port 0 has
* over or underflowed. This bit will remain set until it is reset by
* writing 1 to this bit.
*/
#define DMI_SERCS_ERROR0_OFFSET 2
#define DMI_SERCS_ERROR0_LENGTH 1
#define DMI_SERCS_ERROR0 (0x1U << DMI_SERCS_ERROR0_OFFSET)
/*
* 1 when the core-to-debugger queue for serial port 0 is not empty.
*/
#define DMI_SERCS_VALID0_OFFSET 1
#define DMI_SERCS_VALID0_LENGTH 1
#define DMI_SERCS_VALID0 (0x1U << DMI_SERCS_VALID0_OFFSET)
/*
* 1 when the debugger-to-core queue for serial port 0 is full.
*/
#define DMI_SERCS_FULL0_OFFSET 0
#define DMI_SERCS_FULL0_LENGTH 1
#define DMI_SERCS_FULL0 (0x1U << DMI_SERCS_FULL0_OFFSET)
#define DMI_SERTX 0x35
#define DMI_SERTX_DATA_OFFSET 0
#define DMI_SERTX_DATA_LENGTH 32
#define DMI_SERTX_DATA (0xffffffffU << DMI_SERTX_DATA_OFFSET)
#define DMI_SERRX 0x36
#define DMI_SERRX_DATA_OFFSET 0
#define DMI_SERRX_DATA_LENGTH 32
#define DMI_SERRX_DATA (0xffffffffU << DMI_SERRX_DATA_OFFSET)
+10 -13
View File
@@ -240,6 +240,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
case SLOT1: return 5;
case SLOT_LAST: return info->dramsize-1;
}
break;
case 64:
switch (slot) {
case SLOT0: return 4;
@@ -1407,12 +1408,6 @@ static int strict_step(struct target *target, bool announce)
LOG_DEBUG("enter");
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
riscv_remove_breakpoint(target, breakpoint);
breakpoint = breakpoint->next;
}
struct watchpoint *watchpoint = target->watchpoints;
while (watchpoint) {
riscv_remove_watchpoint(target, watchpoint);
@@ -1423,12 +1418,6 @@ static int strict_step(struct target *target, bool announce)
if (result != ERROR_OK)
return result;
breakpoint = target->breakpoints;
while (breakpoint) {
riscv_add_breakpoint(target, breakpoint);
breakpoint = breakpoint->next;
}
watchpoint = target->watchpoints;
while (watchpoint) {
riscv_add_watchpoint(target, watchpoint);
@@ -1787,6 +1776,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
break;
default:
assert(0);
LOG_ERROR("Got invalid register result %d", result);
goto error;
}
if (riscv_xlen(target) == 32) {
reg_cache_set(target, reg, data & 0xffffffff);
@@ -1847,7 +1838,7 @@ static int handle_halt(struct target *target, bool announce)
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case DCSR_CAUSE_HWBP:
target->debug_reason = DBG_REASON_WPTANDBKPT;
target->debug_reason = DBG_REASON_WATCHPOINT;
/* If we halted because of a data trigger, gdb doesn't know to do
* the disable-breakpoints-step-enable-breakpoints dance. */
info->need_strict_step = true;
@@ -1873,6 +1864,12 @@ static int handle_halt(struct target *target, bool announce)
riscv_enumerate_triggers(target);
}
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
int retval;
if (riscv_semihosting(target, &retval) != 0)
return retval;
}
if (announce)
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+86 -46
View File
@@ -189,8 +189,6 @@ typedef struct {
* go low. */
unsigned int ac_busy_delay;
bool need_strict_step;
bool abstract_read_csr_supported;
bool abstract_write_csr_supported;
bool abstract_read_fpr_supported;
@@ -264,11 +262,18 @@ static dm013_info_t *get_dm(struct target *target)
return dm;
}
static uint32_t hartsel_mask(const struct target *target)
static uint32_t set_hartsel(uint32_t initial, uint32_t index)
{
RISCV013_INFO(info);
/* TODO: Properly handle hartselhi as well*/
return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
initial &= ~DMI_DMCONTROL_HARTSELLO;
initial &= ~DMI_DMCONTROL_HARTSELHI;
uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1);
initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET;
uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH;
assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH);
initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET;
return initial;
}
static void decode_dmi(char *text, unsigned address, unsigned data)
@@ -282,8 +287,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
{ DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
{ DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET, "hartsello" },
/* TODO: hartsellhi */
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" },
{ DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
{ DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
@@ -741,8 +746,8 @@ static int write_abstract_arg(struct target *target, unsigned index,
/**
* @size in bits
*/
static uint32_t access_register_command(uint32_t number, unsigned size,
uint32_t flags)
static uint32_t access_register_command(struct target *target, uint32_t number,
unsigned size, uint32_t flags)
{
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
switch (size) {
@@ -765,8 +770,13 @@ static uint32_t access_register_command(uint32_t number, unsigned size,
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
number - GDB_REGNO_CSR0);
} else {
assert(0);
} else if (number >= GDB_REGNO_COUNT) {
/* Custom register. */
assert(target->reg_cache->reg_list[number].arch_info);
riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info;
assert(reg_info);
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
0xc000 + reg_info->custom_number);
}
command |= flags;
@@ -786,7 +796,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
!info->abstract_read_csr_supported)
return ERROR_FAIL;
uint32_t command = access_register_command(number, size,
uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER);
int result = execute_abstract_command(target, command);
@@ -821,7 +831,7 @@ static int register_write_abstract(struct target *target, uint32_t number,
!info->abstract_write_csr_supported)
return ERROR_FAIL;
uint32_t command = access_register_command(number, size,
uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
@@ -917,22 +927,25 @@ typedef struct {
riscv_addr_t hart_address;
/* Memory address to access the scratch memory from the debugger. */
riscv_addr_t debug_address;
struct working_area *area;
} scratch_mem_t;
/**
* Find some scratch memory to be used with the given program.
*/
static int scratch_find(struct target *target,
static int scratch_reserve(struct target *target,
scratch_mem_t *scratch,
struct riscv_program *program,
unsigned size_bytes)
{
riscv013_info_t *info = get_info(target);
riscv_addr_t alignment = 1;
while (alignment < size_bytes)
alignment *= 2;
scratch->area = NULL;
riscv013_info_t *info = get_info(target);
if (info->dataaccess == 1) {
/* Sign extend dataaddr. */
scratch->hart_address = info->dataaddr;
@@ -963,8 +976,9 @@ static int scratch_find(struct target *target,
return ERROR_OK;
}
if (riscv_use_scratch_ram) {
scratch->hart_address = (riscv_scratch_ram_address + alignment - 1) &
if (target_alloc_working_area(target, size_bytes + alignment - 1,
&scratch->area) == ERROR_OK) {
scratch->hart_address = (scratch->area->address + alignment - 1) &
~(alignment - 1);
scratch->memory_space = SPACE_DMI_RAM;
scratch->debug_address = scratch->hart_address;
@@ -972,10 +986,19 @@ static int scratch_find(struct target *target,
}
LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
"an address with 'riscv set_scratch_ram'.", size_bytes);
"a work area with 'configure -work-area-phys'.", size_bytes);
return ERROR_FAIL;
}
static int scratch_release(struct target *target,
scratch_mem_t *scratch)
{
if (scratch->area)
return target_free_working_area(target, scratch->area);
return ERROR_OK;
}
static int scratch_read64(struct target *target, scratch_mem_t *scratch,
uint64_t *value)
{
@@ -1090,23 +1113,29 @@ static int register_write_direct(struct target *target, unsigned number,
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
scratch_mem_t scratch;
bool use_scratch = false;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a register, so
* we need to use some scratch RAM. */
use_scratch = true;
riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
scratch_mem_t scratch;
if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
return ERROR_FAIL;
if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
!= ERROR_OK)
!= ERROR_OK) {
scratch_release(target, &scratch);
return ERROR_FAIL;
}
if (scratch_write64(target, &scratch, value) != ERROR_OK)
if (scratch_write64(target, &scratch, value) != ERROR_OK) {
scratch_release(target, &scratch);
return ERROR_FAIL;
}
} else {
if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
@@ -1133,6 +1162,9 @@ static int register_write_direct(struct target *target, unsigned number,
reg->valid = true;
}
if (use_scratch)
scratch_release(target, &scratch);
/* Restore S0. */
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
return ERROR_FAIL;
@@ -1209,13 +1241,15 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
0));
if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
return ERROR_FAIL;
use_scratch = true;
if (register_write_direct(target, GDB_REGNO_S0,
scratch.hart_address) != ERROR_OK)
scratch.hart_address) != ERROR_OK) {
scratch_release(target, &scratch);
return ERROR_FAIL;
}
} else if (riscv_supports_extension(target,
riscv_current_hartid(target), 'D')) {
riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
@@ -1234,8 +1268,10 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
/* Don't message on error. Probably the register doesn't exist. */
if (use_scratch) {
if (scratch_read64(target, &scratch, value) != ERROR_OK)
return ERROR_FAIL;
result = scratch_read64(target, &scratch, value);
scratch_release(target, &scratch);
if (result != ERROR_OK)
return result;
} else {
/* Read S0 */
if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
@@ -1290,6 +1326,7 @@ static void deinit_target(struct target *target)
LOG_DEBUG("riscv_deinit_target()");
riscv_info_t *info = (riscv_info_t *) target->arch_info;
free(info->version_specific);
/* TODO: free register arch_info */
info->version_specific = NULL;
}
@@ -1336,8 +1373,8 @@ static int examine(struct target *target)
dm->was_reset = true;
}
uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
uint32_t dmcontrol;
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
@@ -1348,7 +1385,10 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
uint32_t hartsel = get_field(dmcontrol, max_hartsel_mask);
uint32_t hartsel =
(get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) <<
DMI_DMCONTROL_HARTSELLO_LENGTH) |
get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO);
info->hartsellen = 0;
while (hartsel & 1) {
info->hartsellen++;
@@ -1418,8 +1458,7 @@ static int examine(struct target *target)
if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
dmi_write(target, DMI_DMCONTROL,
set_field(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET,
hartsel_mask(target), i));
set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
if (!riscv_is_halted(target)) {
if (riscv013_halt_current_hart(target) != ERROR_OK) {
@@ -1595,7 +1634,7 @@ static int assert_reset(struct target *target)
if (!riscv_hart_enabled(target, i))
continue;
control = set_field(control_base, hartsel_mask(target), i);
control = set_hartsel(control_base, i);
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
dmi_write(target, DMI_DMCONTROL, control);
@@ -1606,8 +1645,7 @@ static int assert_reset(struct target *target)
} else {
/* Reset just this hart. */
uint32_t control = set_field(control_base, hartsel_mask(target),
r->current_hartid);
uint32_t control = set_hartsel(control_base, r->current_hartid);
control = set_field(control, DMI_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
@@ -1630,7 +1668,7 @@ static int deassert_reset(struct target *target)
control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
dmi_write(target, DMI_DMCONTROL,
set_field(control, hartsel_mask(target), r->current_hartid));
set_hartsel(control, r->current_hartid));
uint32_t dmstatus;
int dmi_busy_delay = info->dmi_busy_delay;
@@ -1642,7 +1680,7 @@ static int deassert_reset(struct target *target)
if (!riscv_hart_enabled(target, index))
continue;
dmi_write(target, DMI_DMCONTROL,
set_field(control, hartsel_mask(target), index));
set_hartsel(control, index));
} else {
index = r->current_hartid;
}
@@ -1682,7 +1720,7 @@ static int deassert_reset(struct target *target)
if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) {
/* Ack reset. */
dmi_write(target, DMI_DMCONTROL,
set_field(control, hartsel_mask(target), index) |
set_hartsel(control, index) |
DMI_DMCONTROL_ACKHAVERESET);
}
@@ -2042,9 +2080,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
result = register_write_direct(target, GDB_REGNO_S0, address);
if (result != ERROR_OK)
goto error;
uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_POSTEXEC);
uint32_t command = access_register_command(target, GDB_REGNO_S1,
riscv_xlen(target),
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
result = execute_abstract_command(target, command);
if (result != ERROR_OK)
goto error;
@@ -2530,7 +2568,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
/* Write and execute command that moves value into S1 and
* executes program buffer. */
uint32_t command = access_register_command(GDB_REGNO_S1, 32,
uint32_t command = access_register_command(target,
GDB_REGNO_S1, 32,
AC_ACCESS_REGISTER_POSTEXEC |
AC_ACCESS_REGISTER_TRANSFER |
AC_ACCESS_REGISTER_WRITE);
@@ -2723,9 +2762,10 @@ static int riscv013_select_current_hart(struct target *target)
return ERROR_OK;
uint32_t dmcontrol;
/* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid);
dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
int result = dmi_write(target, DMI_DMCONTROL, dmcontrol);
dm->current_hartid = r->current_hartid;
return result;
@@ -2807,7 +2847,7 @@ static bool riscv013_is_halted(struct target *target)
/* TODO: Can we make this more obvious to eg. a gdb user? */
uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE |
DMI_DMCONTROL_ACKHAVERESET;
dmcontrol = set_field(dmcontrol, hartsel_mask(target), hartid);
dmcontrol = set_hartsel(dmcontrol, hartid);
/* If we had been halted when we reset, request another halt. If we
* ended up running out of reset, then the user will (hopefully) get a
* message that a reset happened, that the target is running, and then
@@ -2950,7 +2990,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
/* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid);
dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
uint32_t dmstatus;
+269 -67
View File
@@ -185,18 +185,19 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
bool riscv_use_scratch_ram;
uint64_t riscv_scratch_ram_address;
bool riscv_prefer_sba;
typedef struct {
uint16_t low, high;
} range_t;
/* In addition to the ones in the standard spec, we'll also expose additional
* CSRs in this list.
* The list is either NULL, or a series of ranges (inclusive), terminated with
* 1,0. */
struct {
uint16_t low, high;
} *expose_csr;
range_t *expose_csr;
/* Same, but for custom registers. */
range_t *expose_custom;
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
@@ -263,6 +264,8 @@ static int riscv_init_target(struct command_context *cmd_ctx,
select_dbus.num_bits = target->tap->ir_length;
select_idcode.num_bits = target->tap->ir_length;
riscv_semihosting_init(target);
return ERROR_OK;
}
@@ -273,8 +276,16 @@ static void riscv_deinit_target(struct target *target)
if (tt) {
tt->deinit_target(target);
riscv_info_t *info = (riscv_info_t *) target->arch_info;
free(info->reg_names);
free(info);
}
/* Free the shared structure use for most registers. */
free(target->reg_cache->reg_list[0].arch_info);
/* Free the ones we allocated separately. */
for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].arch_info);
free(target->reg_cache->reg_list);
free(target->reg_cache);
target->arch_info = NULL;
}
@@ -644,6 +655,89 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}
/* Sets *hit_watchpoint to the first watchpoint identified as causing the
* current halt.
*
* The GDB server uses this information to tell GDB what data address has
* been hit, which enables GDB to print the hit variable along with its old
* and new value. */
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
{
struct watchpoint *wp = target->watchpoints;
LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
/*TODO instead of disassembling the instruction that we think caused the
* trigger, check the hit bit of each watchpoint first. The hit bit is
* simpler and more reliable to check but as it is optional and relatively
* new, not all hardware will implement it */
riscv_reg_t dpc;
riscv_get_register(target, &dpc, GDB_REGNO_DPC);
const uint8_t length = 4;
LOG_DEBUG("dpc is 0x%" PRIx64, dpc);
/* fetch the instruction at dpc */
uint8_t buffer[length];
if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) {
LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc);
return ERROR_FAIL;
}
uint32_t instruction = 0;
for (int i = 0; i < length; i++) {
LOG_DEBUG("Next byte is %x", buffer[i]);
instruction += (buffer[i] << 8 * i);
}
LOG_DEBUG("Full instruction is %x", instruction);
/* find out which memory address is accessed by the instruction at dpc */
/* opcode is first 7 bits of the instruction */
uint8_t opcode = instruction & 0x7F;
uint32_t rs1;
int16_t imm;
riscv_reg_t mem_addr;
if (opcode == MATCH_LB || opcode == MATCH_SB) {
rs1 = (instruction & 0xf8000) >> 15;
riscv_get_register(target, &mem_addr, rs1);
if (opcode == MATCH_SB) {
LOG_DEBUG("%x is store instruction", instruction);
imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20);
} else {
LOG_DEBUG("%x is load instruction", instruction);
imm = (instruction & 0xfff00000) >> 20;
}
/* sign extend 12-bit imm to 16-bits */
if (imm & (1 << 11))
imm |= 0xf000;
mem_addr += imm;
LOG_DEBUG("memory address=0x%" PRIx64, mem_addr);
} else {
LOG_DEBUG("%x is not a RV32I load or store", instruction);
return ERROR_FAIL;
}
while (wp) {
/*TODO support length/mask */
if (wp->address == mem_addr) {
*hit_watchpoint = wp;
LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address);
return ERROR_OK;
}
wp = wp->next;
}
/* No match found - either we hit a watchpoint caused by an instruction that
* this function does not yet disassemble, or we hit a breakpoint.
*
* OpenOCD will behave as if this function had never been implemented i.e.
* report the halt to GDB with no address information. */
return ERROR_FAIL;
}
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
@@ -802,7 +896,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
*reg_list_size = 32;
break;
case REG_CLASS_ALL:
*reg_list_size = GDB_REGNO_COUNT;
*reg_list_size = target->reg_cache->num_regs;
break;
default:
LOG_ERROR("Unsupported reg_class: %d", reg_class);
@@ -1068,9 +1162,17 @@ int riscv_openocd_poll(struct target *target)
if (riscv_rtos_enabled(target)) {
target->rtos->current_threadid = halted_hart + 1;
target->rtos->current_thread = halted_hart + 1;
riscv_set_rtos_hartid(target, halted_hart);
}
target->state = TARGET_HALTED;
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
int retval;
if (riscv_semihosting(target, &retval) != 0)
return retval;
}
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
@@ -1240,31 +1342,6 @@ COMMAND_HANDLER(riscv_test_compliance) {
}
}
COMMAND_HANDLER(riscv_set_scratch_ram)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (!strcmp(CMD_ARGV[0], "none")) {
riscv_use_scratch_ram = false;
return ERROR_OK;
}
/* TODO: use COMMAND_PARSE_NUMBER */
long long unsigned int address;
int result = sscanf(CMD_ARGV[0], "%llx", &address);
if (result != (int) strlen(CMD_ARGV[0])) {
LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]);
riscv_use_scratch_ram = false;
return ERROR_FAIL;
}
riscv_scratch_ram_address = address;
riscv_use_scratch_ram = true;
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_prefer_sba)
{
if (CMD_ARGC != 1) {
@@ -1288,20 +1365,15 @@ void parse_error(const char *string, char c, unsigned position)
LOG_ERROR("%s", buf);
}
COMMAND_HANDLER(riscv_set_expose_csrs)
int parse_ranges(range_t **ranges, const char **argv)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
for (unsigned pass = 0; pass < 2; pass++) {
unsigned range = 0;
unsigned low = 0;
bool parse_low = true;
unsigned high = 0;
for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) {
char c = CMD_ARGV[0][i];
for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
char c = argv[0][i];
if (isspace(c)) {
/* Ignore whitespace. */
continue;
@@ -1315,13 +1387,13 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
parse_low = false;
} else if (c == ',' || c == 0) {
if (pass == 1) {
expose_csr[range].low = low;
expose_csr[range].high = low;
(*ranges)[range].low = low;
(*ranges)[range].high = low;
}
low = 0;
range++;
} else {
parse_error(CMD_ARGV[0], c, i);
parse_error(argv[0], c, i);
return ERROR_COMMAND_SYNTAX_ERROR;
}
@@ -1332,31 +1404,52 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
} else if (c == ',' || c == 0) {
parse_low = true;
if (pass == 1) {
expose_csr[range].low = low;
expose_csr[range].high = high;
(*ranges)[range].low = low;
(*ranges)[range].high = high;
}
low = 0;
high = 0;
range++;
} else {
parse_error(CMD_ARGV[0], c, i);
parse_error(argv[0], c, i);
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
}
if (pass == 0) {
if (expose_csr)
free(expose_csr);
expose_csr = calloc(range + 2, sizeof(*expose_csr));
if (*ranges)
free(*ranges);
*ranges = calloc(range + 2, sizeof(range_t));
} else {
expose_csr[range].low = 1;
expose_csr[range].high = 0;
(*ranges)[range].low = 1;
(*ranges)[range].high = 0;
}
}
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_expose_csrs)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
return parse_ranges(&expose_csr, CMD_ARGV);
}
COMMAND_HANDLER(riscv_set_expose_custom)
{
if (CMD_ARGC != 1) {
LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
return parse_ranges(&expose_custom, CMD_ARGV);
}
COMMAND_HANDLER(riscv_authdata_read)
{
if (CMD_ARGC != 0) {
@@ -1486,13 +1579,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.usage = "riscv set_reset_timeout_sec [sec]",
.help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
},
{
.name = "set_scratch_ram",
.handler = riscv_set_scratch_ram,
.mode = COMMAND_ANY,
.usage = "riscv set_scratch_ram none|[address]",
.help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'."
},
{
.name = "set_prefer_sba",
.handler = riscv_set_prefer_sba,
@@ -1510,6 +1596,15 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"addition to the standard ones. This must be executed before "
"`init`."
},
{
.name = "expose_custom",
.handler = riscv_set_expose_custom,
.mode = COMMAND_ANY,
.usage = "riscv expose_custom n0[-m0][,n1[-m1]]...",
.help = "Configure a list of inclusive ranges for custom registers to "
"expose. custom0 is accessed as abstract register number 0xc000, "
"etc. This must be executed before `init`."
},
{
.name = "authdata_read",
.handler = riscv_authdata_read,
@@ -1541,6 +1636,56 @@ static const struct command_registration riscv_exec_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
extern __COMMAND_HANDLER(handle_common_semihosting_command);
extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
/*
* To be noted that RISC-V targets use the same semihosting commands as
* ARM targets.
*
* The main reason is compatibility with existing tools. For example the
* Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to
* configure semihosting, which generate commands like `arm semihosting
* enable`.
* A secondary reason is the fact that the protocol used is exactly the
* one specified by ARM. If RISC-V will ever define its own semihosting
* protocol, then a command like `riscv semihosting enable` will make
* sense, but for now all semihosting commands are prefixed with `arm`.
*/
static const struct command_registration arm_exec_command_handlers[] = {
{
"semihosting",
.handler = handle_common_semihosting_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting operations",
},
{
"semihosting_cmdline",
.handler = handle_common_semihosting_cmdline,
.mode = COMMAND_EXEC,
.usage = "arguments",
.help = "command line arguments to be passed to program",
},
{
"semihosting_fileio",
.handler = handle_common_semihosting_fileio_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting fileio operations",
},
{
"semihosting_resexit",
.handler = handle_common_semihosting_resumable_exit_command,
.mode = COMMAND_EXEC,
.usage = "['enable'|'disable']",
.help = "activate support for semihosting resumable exit",
},
COMMAND_REGISTRATION_DONE
};
const struct command_registration riscv_command_handlers[] = {
{
.name = "riscv",
@@ -1549,6 +1694,13 @@ const struct command_registration riscv_command_handlers[] = {
.usage = "",
.chain = riscv_exec_command_handlers
},
{
.name = "arm",
.mode = COMMAND_ANY,
.help = "ARM Command Group",
.usage = "",
.chain = arm_exec_command_handlers
},
COMMAND_REGISTRATION_DONE
};
@@ -1581,6 +1733,7 @@ struct target_type riscv_target = {
.add_watchpoint = riscv_add_watchpoint,
.remove_watchpoint = riscv_remove_watchpoint,
.hit_watchpoint = riscv_hit_watchpoint,
.arch_state = riscv_arch_state,
@@ -1760,7 +1913,7 @@ void riscv_invalidate_register_cache(struct target *target)
RISCV_INFO(r);
register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i];
reg->valid = false;
}
@@ -2033,7 +2186,8 @@ const char *gdb_regno_name(enum gdb_regno regno)
static int register_get(struct reg *reg)
{
struct target *target = (struct target *) reg->arch_info;
riscv_reg_info_t *reg_info = reg->arch_info;
struct target *target = reg_info->target;
uint64_t value;
int result = riscv_get_register(target, &value, reg->number);
if (result != ERROR_OK)
@@ -2044,7 +2198,8 @@ static int register_get(struct reg *reg)
static int register_set(struct reg *reg, uint8_t *buf)
{
struct target *target = (struct target *) reg->arch_info;
riscv_reg_info_t *reg_info = reg->arch_info;
struct target *target = reg_info->target;
uint64_t value = buf_get_u64(buf, 0, reg->size);
@@ -2086,12 +2241,26 @@ int riscv_init_registers(struct target *target)
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
if (expose_custom) {
for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
for (unsigned number = expose_custom[i].low;
number <= expose_custom[i].high;
number++)
target->reg_cache->num_regs++;
}
}
LOG_DEBUG("create register cache for %d registers",
target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
if (info->reg_names)
free(info->reg_names);
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
info->reg_names =
calloc(target->reg_cache->num_regs, max_reg_name_len);
char *reg_name = info->reg_names;
static struct reg_feature feature_cpu = {
@@ -2106,6 +2275,9 @@ int riscv_init_registers(struct target *target)
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
static struct reg_feature feature_custom = {
.name = "org.gnu.gdb.riscv.custom"
};
static struct reg_data_type type_ieee_single = {
.type = REG_TYPE_IEEE_SINGLE,
@@ -2124,18 +2296,24 @@ int riscv_init_registers(struct target *target)
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
unsigned csr_info_index = 0;
/* When gdb request register N, gdb_get_register_packet() assumes that this
unsigned custom_range_index = 0;
int custom_within_range = 0;
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
shared_reg_info->target = target;
/* When gdb requests register N, gdb_get_register_packet() assumes that this
* is register at index N in reg_list. So if there are certain registers
* that don't exist, we need to leave holes in the list (or renumber, but
* it would be nice not to have yet another set of numbers to translate
* between). */
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
struct reg *r = &target->reg_cache->reg_list[number];
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = target;
r->arch_info = shared_reg_info;
r->number = number;
r->size = riscv_xlen(target);
/* r->size is set in riscv_invalidate_register_cache, maybe because the
@@ -2495,11 +2673,35 @@ int riscv_init_registers(struct target *target)
r->group = "general";
r->feature = &feature_virtual;
r->size = 8;
} else {
/* Custom registers. */
assert(expose_custom);
range_t *range = &expose_custom[custom_range_index];
assert(range->low <= range->high);
unsigned custom_number = range->low + custom_within_range;
r->group = "custom";
r->feature = &feature_custom;
r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
assert(r->arch_info);
((riscv_reg_info_t *) r->arch_info)->target = target;
((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
sprintf(reg_name, "custom%d", custom_number);
custom_within_range++;
if (custom_within_range > range->high - range->low) {
custom_within_range = 0;
custom_range_index++;
}
}
if (reg_name[0])
r->name = reg_name;
reg_name += strlen(reg_name) + 1;
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
assert(reg_name < info->reg_names + target->reg_cache->num_regs *
max_reg_name_len);
r->value = &info->reg_cache_values[number];
}
+13 -5
View File
@@ -7,7 +7,7 @@ struct riscv_program;
#include "opcodes.h"
#include "gdb_regs.h"
/* The register cache is staticly allocated. */
/* The register cache is statically allocated. */
#define RISCV_MAX_HARTS 32
#define RISCV_MAX_REGISTERS 5000
#define RISCV_MAX_TRIGGERS 32
@@ -35,6 +35,11 @@ enum riscv_halt_reason {
RISCV_HALT_ERROR
};
typedef struct {
struct target *target;
unsigned custom_number;
} riscv_reg_info_t;
typedef struct {
unsigned dtm_version;
@@ -58,7 +63,9 @@ typedef struct {
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
/* The register cache points into here. */
/* OpenOCD's register cache points into here. This is not per-hart because
* we just invalidate the entire cache when we change which hart is
* selected. */
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
/* Single buffer that contains all register names, instead of calling
@@ -129,9 +136,6 @@ extern int riscv_command_timeout_sec;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
extern int riscv_reset_timeout_sec;
extern bool riscv_use_scratch_ram;
extern uint64_t riscv_scratch_ram_address;
extern bool riscv_prefer_sba;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
@@ -257,7 +261,11 @@ int riscv_remove_breakpoint(struct target *target,
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint);
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address);
int riscv_init_registers(struct target *target);
void riscv_semihosting_init(struct target *target);
int riscv_semihosting(struct target *target, int *retval);
#endif
+194
View File
@@ -0,0 +1,194 @@
/***************************************************************************
* Copyright (C) 2018 by Liviu Ionescu *
* ilg@livius.net *
* *
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
* Written by Nicolas Pitre <nico@marvell.com> *
* *
* Copyright (C) 2010 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2016 by Square, Inc. *
* Steven Stallion <stallion@squareup.com> *
* *
* 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 2 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/>. *
***************************************************************************/
/**
* @file
* Hold RISC-V semihosting support.
*
* The RISC-V code is inspired from ARM semihosting.
*
* Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
* from ARM Ltd.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "log.h"
#include "target/target.h"
#include "target/semihosting_common.h"
#include "riscv.h"
static int riscv_semihosting_setup(struct target *target, int enable);
static int riscv_semihosting_post_result(struct target *target);
/**
* Initialize RISC-V semihosting. Use common ARM code.
*/
void riscv_semihosting_init(struct target *target)
{
semihosting_common_init(target, riscv_semihosting_setup,
riscv_semihosting_post_result);
}
/**
* Check for and process a semihosting request using the ARM protocol). This
* is meant to be called when the target is stopped due to a debug mode entry.
* If the value 0 is returned then there was nothing to process. A non-zero
* return value signifies that a request was processed and the target resumed,
* or an error was encountered, in which case the caller must return
* immediately.
*
* @param target Pointer to the target to process.
* @param retval Pointer to a location where the return code will be stored
* @return non-zero value if a request was processed or an error encountered
*/
int riscv_semihosting(struct target *target, int *retval)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting)
return 0;
if (!semihosting->is_active)
return 0;
riscv_reg_t dpc;
int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
if (result != ERROR_OK)
return 0;
uint8_t tmp[12];
/* Read the current instruction, including the bracketing */
*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
if (*retval != ERROR_OK)
return 0;
/*
* The instructions that trigger a semihosting call,
* always uncompressed, should look like:
*
* 01f01013 slli zero,zero,0x1f
* 00100073 ebreak
* 40705013 srai zero,zero,0x7
*/
uint32_t pre = target_buffer_get_u32(target, tmp);
uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
uint32_t post = target_buffer_get_u32(target, tmp + 8);
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
/* Not the magic sequence defining semihosting. */
return 0;
}
/*
* Perform semihosting call if we are not waiting on a fileio
* operation to complete.
*/
if (!semihosting->hit_fileio) {
/* RISC-V uses A0 and A1 to pass function arguments */
riscv_reg_t r0;
riscv_reg_t r1;
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK)
return 0;
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK)
return 0;
semihosting->op = r0;
semihosting->param = r1;
semihosting->word_size_bytes = riscv_xlen(target) / 8;
/* Check for ARM operation numbers. */
if (0 <= semihosting->op && semihosting->op <= 0x31) {
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed semihosting operation");
return 0;
}
} else {
/* Unknown operation number, not a semihosting call. */
return 0;
}
}
/*
* Resume target if we are not waiting on a fileio
* operation to complete.
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
/* Resume right after the EBREAK 4 bytes instruction. */
*retval = target_resume(target, 0, dpc+4, 0, 0);
if (*retval != ERROR_OK) {
LOG_ERROR("Failed to resume target");
return 0;
}
return 1;
}
return 0;
}
/* -------------------------------------------------------------------------
* Local functions. */
/**
* Called via semihosting->setup() later, after the target is known,
* usually on the first semihosting command.
*/
static int riscv_semihosting_setup(struct target *target, int enable)
{
LOG_DEBUG("enable=%d", enable);
struct semihosting *semihosting = target->semihosting;
if (semihosting)
semihosting->setup_time = clock();
return ERROR_OK;
}
static int riscv_semihosting_post_result(struct target *target)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting) {
/* If not enabled, silently ignored. */
return 0;
}
LOG_DEBUG("0x%" PRIx64, semihosting->result);
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
return 0;
}
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
/***************************************************************************
* Copyright (C) 2018 by Liviu Ionescu *
* <ilg@livius.net> *
* *
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
* Written by Nicolas Pitre <nico@marvell.com> *
* *
* 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 2 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/>. *
***************************************************************************/
#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H
#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
/*
* According to:
* "Semihosting for AArch32 and AArch64, Release 2.0"
* https://static.docs.arm.com/100863/0200/semihosting.pdf
* from ARM Ltd.
*
* The available semihosting operation numbers passed in R0 are allocated
* as follows:
* - 0x00-0x31 Used by ARM.
* - 0x32-0xFF Reserved for future use by ARM.
* - 0x100-0x1FF Reserved for user applications. These are not used by ARM.
* However, if you are writing your own SVC operations, you are advised
* to use a different SVC number rather than using the semihosted
* SVC number and these operation type numbers.
* - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended
* that you do not use these.
*/
enum semihosting_operation_numbers {
/*
* ARM semihosting operations, in lexicographic order.
*/
SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */
SEMIHOSTING_SYS_CLOSE = 0x02,
SEMIHOSTING_SYS_CLOCK = 0x10,
SEMIHOSTING_SYS_ELAPSED = 0x30,
SEMIHOSTING_SYS_ERRNO = 0x13,
SEMIHOSTING_SYS_EXIT = 0x18,
SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
SEMIHOSTING_SYS_FLEN = 0x0C,
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
SEMIHOSTING_SYS_HEAPINFO = 0x16,
SEMIHOSTING_SYS_ISERROR = 0x08,
SEMIHOSTING_SYS_ISTTY = 0x09,
SEMIHOSTING_SYS_OPEN = 0x01,
SEMIHOSTING_SYS_READ = 0x06,
SEMIHOSTING_SYS_READC = 0x07,
SEMIHOSTING_SYS_REMOVE = 0x0E,
SEMIHOSTING_SYS_RENAME = 0x0F,
SEMIHOSTING_SYS_SEEK = 0x0A,
SEMIHOSTING_SYS_SYSTEM = 0x12,
SEMIHOSTING_SYS_TICKFREQ = 0x31,
SEMIHOSTING_SYS_TIME = 0x11,
SEMIHOSTING_SYS_TMPNAM = 0x0D,
SEMIHOSTING_SYS_WRITE = 0x05,
SEMIHOSTING_SYS_WRITEC = 0x03,
SEMIHOSTING_SYS_WRITE0 = 0x04,
};
/*
* Codes used by SEMIHOSTING_SYS_EXIT (formerly
* SEMIHOSTING_REPORT_EXCEPTION).
* On 64-bits, the exit code is passed explicitly.
*/
enum semihosting_reported_exceptions {
/* On 32 bits, use it for exit(0) */
ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38),
/* On 32 bits, use it for exit(1) */
ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35),
};
struct target;
/*
* A pointer to this structure was added to the target structure.
*/
struct semihosting {
/** A flag reporting whether semihosting is active. */
bool is_active;
/** A flag reporting whether semihosting fileio is active. */
bool is_fileio;
/** A flag reporting whether semihosting fileio operation is active. */
bool hit_fileio;
/** Most are resumable, except the two exit calls. */
bool is_resumable;
/**
* When SEMIHOSTING_SYS_EXIT is called outside a debug session,
* things are simple, the openocd process calls exit() and passes
* the value returned by the target.
* When SEMIHOSTING_SYS_EXIT is called during a debug session,
* by default execution returns to the debugger, leaving the
* debugger in a HALT state, similar to the state entered when
* encountering a break.
* In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT
* return normally, as any semihosting call, and do not break
* to the debugger.
* The standard allows this to happen, but the condition
* to trigger it is a bit obscure ("by performing an RDI_Execute
* request or equivalent").
*
* To make the SEMIHOSTING_SYS_EXIT call return normally, enable
* this variable via the dedicated command (default: disabled).
*/
bool has_resumable_exit;
/** The Target (hart) word size; 8 for 64-bits targets. */
size_t word_size_bytes;
/** The current semihosting operation (R0 on ARM). */
int op;
/** The current semihosting parameter (R1 or ARM). */
uint64_t param;
/**
* The current semihosting result to be returned to the application.
* Usually 0 for success, -1 for error,
* but sometimes a useful value, even a pointer.
*/
int64_t result;
/** The value to be returned by semihosting SYS_ERRNO request. */
int sys_errno;
/** The semihosting command line to be passed to the target. */
char *cmdline;
/** The current time when 'execution starts' */
clock_t setup_time;
int (*setup)(struct target *target, int enable);
int (*post_result)(struct target *target);
};
int semihosting_common_init(struct target *target, void *setup,
void *post_result);
int semihosting_common(struct target *target);
#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */
+7 -5
View File
@@ -1895,6 +1895,9 @@ static void target_destroy(struct target *target)
if (target->type->deinit_target)
target->type->deinit_target(target);
if (target->semihosting)
free(target->semihosting);
jtag_unregister_event_callback(jtag_enable_callback, target);
struct target_event_action *teap = target->event_action;
@@ -4143,8 +4146,9 @@ static int target_mem2array(Jim_Interp *interp, struct target *target, int argc,
* argv[3] = memory address
* argv[4] = count of times to read
*/
if (argc < 4 || argc > 5) {
Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems [phys]");
Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]");
return JIM_ERR;
}
varname = Jim_GetString(argv[0], &len);
@@ -5448,21 +5452,19 @@ static const struct command_registration target_instance_command_handlers[] = {
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine,
.help = "used internally for reset processing",
.usage = "arp_examine ['allow-defer']",
.usage = "['allow-defer']",
},
{
.name = "was_examined",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_was_examined,
.help = "used internally for reset processing",
.usage = "was_examined",
},
{
.name = "examine_deferred",
.mode = COMMAND_EXEC,
.jim_handler = jim_target_examine_deferred,
.help = "used internally for reset processing",
.usage = "examine_deferred",
},
{
.name = "arp_halt_gdb",
@@ -6429,7 +6431,7 @@ static const struct command_registration target_exec_command_handlers[] = {
.handler = handle_bp_command,
.mode = COMMAND_EXEC,
.help = "list or set hardware or software breakpoint",
.usage = "<address> [<asid>]<length> ['hw'|'hw_ctx']",
.usage = "<address> [<asid>] <length> ['hw'|'hw_ctx']",
},
{
.name = "rbp",
+7 -4
View File
@@ -205,6 +205,9 @@ struct target {
/* file-I/O information for host to do syscall */
struct gdb_fileio_info *fileio_info;
/* The semihosting information, extracted from the target. */
struct semihosting *semihosting;
};
struct target_list {
@@ -214,10 +217,10 @@ struct target_list {
struct gdb_fileio_info {
char *identifier;
uint32_t param_1;
uint32_t param_2;
uint32_t param_3;
uint32_t param_4;
uint64_t param_1;
uint64_t param_2;
uint64_t param_3;
uint64_t param_4;
};
/** Returns the instance-specific name of the specified target. */