OpenOCD
riscv_semihosting.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /***************************************************************************
4  * Copyright (C) 2018 by Liviu Ionescu *
5  * ilg@livius.net *
6  * *
7  * Copyright (C) 2009 by Marvell Technology Group Ltd. *
8  * Written by Nicolas Pitre <nico@marvell.com> *
9  * *
10  * Copyright (C) 2010 by Spencer Oliver *
11  * spen@spen-soft.co.uk *
12  * *
13  * Copyright (C) 2016 by Square, Inc. *
14  * Steven Stallion <stallion@squareup.com> *
15  ***************************************************************************/
16 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <helper/log.h>
32 
33 #include "target/target.h"
34 #include "riscv.h"
35 #include "riscv_reg.h"
36 
37 static int riscv_semihosting_setup(struct target *target, int enable);
38 static int riscv_semihosting_post_result(struct target *target);
39 
41  const target_addr_t pc, bool *sequence_found)
42 {
43  assert(sequence_found);
44 
45  /* The semihosting "magic" sequence must be the exact three instructions
46  * listed below. All these instructions, including the ebreak, must be
47  * uncompressed (4 bytes long). */
48  const uint32_t magic[] = {
49  0x01f01013, /* slli zero,zero,0x1f */
50  0x00100073, /* ebreak */
51  0x40705013 /* srai zero,zero,0x7 */
52  };
53 
54  LOG_TARGET_DEBUG(target, "Checking for RISC-V semihosting sequence "
55  "at PC = 0x%" TARGET_PRIxADDR, pc);
56 
57  /* Read three uncompressed instructions:
58  * The previous, the current one (pointed to by PC) and the next one. */
59  const target_addr_t sequence_start_address = pc - 4;
60  for (int i = 0; i < 3; i++) {
61  uint8_t buf[4];
62 
63  /* Instruction memories may not support arbitrary read size.
64  * Use any size that will work. */
65  const target_addr_t address = sequence_start_address + (4 * i);
66  int result = riscv_read_by_any_size(target, address, 4, buf);
67  if (result != ERROR_OK) {
68  *sequence_found = false;
69  return result;
70  }
71 
72  /* RISC-V instruction layout in memory is always little endian,
73  * regardless of the endianness of the whole system. */
74  const uint32_t value = le_to_h_u32(buf);
75 
76  LOG_TARGET_DEBUG(target, "compare 0x%08" PRIx32 " from 0x%" PRIx64 " against 0x%08" PRIx32,
77  value, address, magic[i]);
78  if (value != magic[i]) {
79  LOG_TARGET_DEBUG(target, "Not a RISC-V semihosting sequence");
80  *sequence_found = false;
81  return ERROR_OK;
82  }
83  }
84 
85  LOG_TARGET_DEBUG(target, "RISC-V semihosting sequence found "
86  "at PC = 0x%" TARGET_PRIxADDR, pc);
87  *sequence_found = true;
88  return ERROR_OK;
89 }
90 
95 {
98 }
99 
108 enum semihosting_result riscv_semihosting(struct target *target, int *retval)
109 {
111  assert(semihosting);
112 
113  riscv_reg_t pc;
115  if (result != ERROR_OK) {
116  LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read PC)");
117  return SEMIHOSTING_ERROR;
118  }
119 
120  bool sequence_found = false;
121  *retval = riscv_semihosting_detect_magic_sequence(target, pc, &sequence_found);
122  if (*retval != ERROR_OK) {
123  LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (during magic seq. detection)");
124  return SEMIHOSTING_ERROR;
125  }
126 
127  if (!semihosting->is_active) {
128  if (sequence_found) {
129  // If semihositing is encountered but disabled, provide an additional hint to the user.
130  LOG_TARGET_WARNING(target, "RISC-V semihosting call encountered in the program "
131  "but semihosting is disabled!");
132  LOG_TARGET_WARNING(target, "The target will remain halted (PC = 0x%" TARGET_PRIxADDR ").", pc);
133  LOG_TARGET_WARNING(target, "Hint: Restart your debug session and enable semihosting "
134  "by command 'arm semihosting enable'.");
135  // TODO: This can be improved: The ebreak halt cause detection and riscv_semihosting() call
136  // can be added also to "arm semihosting enable", which would allow the user to continue
137  // without restart of the debug session.
138  }
139 
140  LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (semihosting not enabled)");
141  return SEMIHOSTING_NONE;
142  }
143 
144  if (!sequence_found) {
145  LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (no magic sequence)");
146  return SEMIHOSTING_NONE;
147  }
148 
149  /* Otherwise we have a semihosting call (and semihosting is enabled).
150  * Proceed with the handling of semihosting. */
151 
152  /*
153  * Perform semihosting call if we are not waiting on a fileio
154  * operation to complete.
155  */
156  if (!semihosting->hit_fileio) {
157  /* RISC-V uses A0 and A1 to pass function arguments */
158  riscv_reg_t r0;
159  riscv_reg_t r1;
160 
162  if (result != ERROR_OK) {
163  LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
164  LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a0)");
165  return SEMIHOSTING_ERROR;
166  }
167 
169  if (result != ERROR_OK) {
170  LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
171  LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a1)");
172  return SEMIHOSTING_ERROR;
173  }
174 
175  semihosting->op = r0;
176  semihosting->param = r1;
178 
179  /* Check for ARM operation numbers. */
180  if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
181  (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
182 
183  *retval = semihosting_common(target);
184  if (*retval != ERROR_OK) {
185  LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op);
186  LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (error during semihosting processing)");
187  return SEMIHOSTING_ERROR;
188  }
189  } else {
190  /* Unknown operation number, not a semihosting call. */
191  LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op);
192  LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (unknown semihosting opcode)");
193  return SEMIHOSTING_NONE;
194  }
195  }
196 
197  /* Resume right after the EBREAK 4 bytes instruction. */
198  *retval = riscv_reg_set(target, GDB_REGNO_PC, pc + 4);
199  if (*retval != ERROR_OK)
200  return SEMIHOSTING_ERROR;
201 
202  /*
203  * Resume target if we are not waiting on a fileio
204  * operation to complete.
205  */
207  LOG_TARGET_DEBUG(target, "Semihosting outcome: HANDLED");
208  return SEMIHOSTING_HANDLED;
209  }
210 
211  LOG_TARGET_DEBUG(target, "Semihosting outcome: WAITING");
212  return SEMIHOSTING_WAITING;
213 }
214 
215 /* -------------------------------------------------------------------------
216  * Local functions. */
217 
222 static int riscv_semihosting_setup(struct target *target, int enable)
223 {
224  LOG_TARGET_DEBUG(target, "enable=%d", enable);
225 
227  assert(semihosting);
228 
229  semihosting->setup_time = clock();
230  return ERROR_OK;
231 }
232 
234 {
236  assert(semihosting);
237 
238  LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
240  return 0;
241 }
uint32_t address
Starting address. Sector aligned.
Definition: dw-spi-helper.h:0
@ GDB_REGNO_A1
Definition: gdb_regs.h:23
@ GDB_REGNO_A0
Definition: gdb_regs.h:22
@ GDB_REGNO_PC
Definition: gdb_regs.h:47
#define LOG_TARGET_WARNING(target, fmt_str,...)
Definition: log.h:160
#define LOG_TARGET_ERROR(target, fmt_str,...)
Definition: log.h:163
#define LOG_TARGET_DEBUG(target, fmt_str,...)
Definition: log.h:151
#define ERROR_OK
Definition: log.h:169
unsigned int riscv_xlen(const struct target *target)
Definition: riscv.c:6060
int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
Read one memory item using any memory access size that will work.
Definition: riscv.c:1579
uint64_t riscv_reg_t
Definition: riscv.h:46
int riscv_reg_set(struct target *target, enum gdb_regno regid, riscv_reg_t value)
This function is used to change the value of a register.
Definition: riscv_reg.c:918
int riscv_reg_get(struct target *target, riscv_reg_t *value, enum gdb_regno regid)
This function is used to get the value of a register.
Definition: riscv_reg.c:952
static int riscv_semihosting_post_result(struct target *target)
void riscv_semihosting_init(struct target *target)
Initialize RISC-V semihosting.
enum semihosting_result riscv_semihosting(struct target *target, int *retval)
Check for and process a semihosting request using the ARM protocol).
static int riscv_semihosting_setup(struct target *target, int enable)
Called via semihosting->setup() later, after the target is known, usually on the first semihosting co...
static int riscv_semihosting_detect_magic_sequence(struct target *target, const target_addr_t pc, bool *sequence_found)
int semihosting_common_init(struct target *target, void *setup, void *post_result)
Initialize common semihosting support.
int semihosting_common(struct target *target)
Portable implementation of ARM semihosting calls.
semihosting_result
@ SEMIHOSTING_ERROR
@ SEMIHOSTING_HANDLED
@ SEMIHOSTING_WAITING
@ SEMIHOSTING_NONE
bool is_resumable
Most are resumable, except the two exit calls.
bool hit_fileio
A flag reporting whether semihosting fileio operation is active.
size_t word_size_bytes
The Target (hart) word size; 8 for 64-bits targets.
int64_t result
The current semihosting result to be returned to the application.
bool is_active
A flag reporting whether semihosting is active.
int op
The current semihosting operation (R0 on ARM).
uint64_t param
The current semihosting parameter (R1 or ARM).
clock_t setup_time
The current time when 'execution starts'.
Definition: target.h:119
struct semihosting * semihosting
Definition: target.h:212
uint64_t target_addr_t
Definition: types.h:279
static uint32_t le_to_h_u32(const uint8_t *buf)
Definition: types.h:112
#define TARGET_PRIxADDR
Definition: types.h:284