aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2018-06-12 14:09:59 -0700
committerGitHub <noreply@github.com>2018-06-12 14:09:59 -0700
commit5d9f48640461fbac5c30958f63a01e666ee64dd4 (patch)
treeef5337bbcfbcc7a1c9134852f10894e6ca662e46 /src
parentf40665c3588c97c609b5e3c8a5e1b5f2a481ef7a (diff)
parent45921eecd832f6529cbb2facc9d5c0e7a04472b5 (diff)
downloadriscv-openocd-5d9f48640461fbac5c30958f63a01e666ee64dd4.zip
riscv-openocd-5d9f48640461fbac5c30958f63a01e666ee64dd4.tar.gz
riscv-openocd-5d9f48640461fbac5c30958f63a01e666ee64dd4.tar.bz2
Merge pull request #266 from gnu-mcu-eclipse/semihosting
target/riscv: add semihosting support
Diffstat (limited to 'src')
-rw-r--r--src/target/Makefile.am1
-rw-r--r--src/target/riscv/riscv-011.c6
-rw-r--r--src/target/riscv/riscv.c66
-rw-r--r--src/target/riscv/riscv.h3
-rw-r--r--src/target/riscv/riscv_semihosting.c194
5 files changed, 270 insertions, 0 deletions
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index a37b717..ad5e162 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -141,6 +141,7 @@ RISCV_SRC = \
%D%/riscv/riscv-011.c \
%D%/riscv/riscv-013.c \
%D%/riscv/riscv.c \
+ %D%/riscv/riscv_semihosting.c \
%D%/riscv/program.c \
%D%/riscv/batch.c
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index 4322f7b..f2a6869 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -1873,6 +1873,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);
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index a0270a3..f6a1a8d 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -263,6 +263,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;
}
@@ -1071,6 +1073,13 @@ int riscv_openocd_poll(struct target *target)
}
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;
}
@@ -1515,6 +1524,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",
@@ -1523,6 +1582,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
};
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index f13549c..4fb0f77 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -257,4 +257,7 @@ int riscv_remove_watchpoint(struct target *target,
int riscv_init_registers(struct target *target);
+void riscv_semihosting_init(struct target *target);
+int riscv_semihosting(struct target *target, int *retval);
+
#endif
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
new file mode 100644
index 0000000..c4b6653
--- /dev/null
+++ b/src/target/riscv/riscv_semihosting.c
@@ -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;
+}