Merge pull request #266 from gnu-mcu-eclipse/semihosting
target/riscv: add semihosting support
This commit is contained in:
commit
5d9f486404
|
@ -141,6 +141,7 @@ RISCV_SRC = \
|
||||||
%D%/riscv/riscv-011.c \
|
%D%/riscv/riscv-011.c \
|
||||||
%D%/riscv/riscv-013.c \
|
%D%/riscv/riscv-013.c \
|
||||||
%D%/riscv/riscv.c \
|
%D%/riscv/riscv.c \
|
||||||
|
%D%/riscv/riscv_semihosting.c \
|
||||||
%D%/riscv/program.c \
|
%D%/riscv/program.c \
|
||||||
%D%/riscv/batch.c
|
%D%/riscv/batch.c
|
||||||
|
|
||||||
|
|
|
@ -1873,6 +1873,12 @@ static int handle_halt(struct target *target, bool announce)
|
||||||
riscv_enumerate_triggers(target);
|
riscv_enumerate_triggers(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
|
||||||
|
int retval;
|
||||||
|
if (riscv_semihosting(target, &retval) != 0)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
if (announce)
|
if (announce)
|
||||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||||
|
|
||||||
|
|
|
@ -263,6 +263,8 @@ static int riscv_init_target(struct command_context *cmd_ctx,
|
||||||
select_dbus.num_bits = target->tap->ir_length;
|
select_dbus.num_bits = target->tap->ir_length;
|
||||||
select_idcode.num_bits = target->tap->ir_length;
|
select_idcode.num_bits = target->tap->ir_length;
|
||||||
|
|
||||||
|
riscv_semihosting_init(target);
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,6 +1073,13 @@ int riscv_openocd_poll(struct target *target)
|
||||||
}
|
}
|
||||||
|
|
||||||
target->state = TARGET_HALTED;
|
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);
|
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -1515,6 +1524,56 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||||
COMMAND_REGISTRATION_DONE
|
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[] = {
|
const struct command_registration riscv_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.name = "riscv",
|
.name = "riscv",
|
||||||
|
@ -1523,6 +1582,13 @@ const struct command_registration riscv_command_handlers[] = {
|
||||||
.usage = "",
|
.usage = "",
|
||||||
.chain = riscv_exec_command_handlers
|
.chain = riscv_exec_command_handlers
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "arm",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "ARM Command Group",
|
||||||
|
.usage = "",
|
||||||
|
.chain = arm_exec_command_handlers
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -257,4 +257,7 @@ int riscv_remove_watchpoint(struct target *target,
|
||||||
|
|
||||||
int riscv_init_registers(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
|
#endif
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue