Skip to content

Commit

Permalink
RISC-V Semihosting 2 of 3: Refactor magic sequence detection
Browse files Browse the repository at this point in the history
Refactor (clean up) the code in riscv_semihosting.c by moving
the magic sequence detection to its own function.

Cleanup the debug prints denoting the semihosting outcome
so that they are easier to understand when reading the OpenOCD's
verbose (debug) log.

Use le_to_h_u32() to convert memory buffer to instruction code
because RISC-V instructions are always little endian.
(target_buffer_get_u32() was incorrect for that reason.)

Change-Id: I3a3ce991336ceeeff023d459d0e28558059554e0
Signed-off-by: Jan Matyas <[email protected]>
  • Loading branch information
JanMatCodasip committed Jan 20, 2025
1 parent eb9ba21 commit 8c0a1cd
Showing 1 changed file with 73 additions and 28 deletions.
101 changes: 73 additions & 28 deletions src/target/riscv/riscv_semihosting.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,57 @@
static int riscv_semihosting_setup(struct target *target, int enable);
static int riscv_semihosting_post_result(struct target *target);

static int riscv_semihosting_detect_magic_sequence(struct target *target,
const target_addr_t pc, bool *sequence_found)
{
assert(sequence_found);

/* The semihosting "magic" sequence must be the exact three instructions
* listed below. All these instructions, including the ebreak, must be
* uncompressed (4 bytes long). */
const uint32_t magic[] = {
0x01f01013, /* slli zero,zero,0x1f */
0x00100073, /* ebreak */
0x40705013 /* srai zero,zero,0x7 */
};

LOG_TARGET_DEBUG(target, "Checking for RISC-V semihosting sequence "
"at PC = 0x%" TARGET_PRIxADDR, pc);

/* Read three uncompressed instructions:
* The previous, the current one (pointed to by PC) and the next one. */
const target_addr_t sequence_start_address = pc - 4;
for (int i = 0; i < 3; i++) {
uint8_t buf[4];

/* Instruction memories may not support arbitrary read size.
* Use any size that will work. */
const target_addr_t address = sequence_start_address + (4 * i);
int result = riscv_read_by_any_size(target, address, 4, buf);
if (result != ERROR_OK) {
*sequence_found = false;
return result;
}

/* RISC-V instruction layout in memory is always little endian,
* regardless of the endianness of the whole system. */
const uint32_t value = le_to_h_u32(buf);

LOG_TARGET_DEBUG(target, "compare 0x%08" PRIx32 " from 0x%" PRIx64 " against 0x%08" PRIx32,
value, address, magic[i]);
if (value != magic[i]) {
LOG_TARGET_DEBUG(target, "Not a RISC-V semihosting sequence");
*sequence_found = false;
return ERROR_OK;
}
}

LOG_TARGET_DEBUG(target, "RISC-V semihosting sequence found "
"at PC = 0x%" TARGET_PRIxADDR, pc);
*sequence_found = true;
return ERROR_OK;
}

/**
* Initialize RISC-V semihosting. Use common ARM code.
*/
Expand All @@ -60,42 +111,32 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
assert(semihosting);

if (!semihosting->is_active) {
LOG_TARGET_DEBUG(target, " -> NONE (!semihosting->is_active)");
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (semihosting not enabled)");
return SEMIHOSTING_NONE;
}

riscv_reg_t pc;
int result = riscv_reg_get(target, &pc, GDB_REGNO_PC);
if (result != ERROR_OK)
if (result != ERROR_OK) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read PC)");
return SEMIHOSTING_ERROR;
}

/*
* The instructions that trigger a semihosting call,
* always uncompressed, should look like:
*/
uint32_t magic[] = {
0x01f01013, /* slli zero,zero,0x1f */
0x00100073, /* ebreak */
0x40705013 /* srai zero,zero,0x7 */
};
bool sequence_found;
*retval = riscv_semihosting_detect_magic_sequence(target, pc, &sequence_found);
if (*retval != ERROR_OK) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (during magic seq. detection)");
return SEMIHOSTING_ERROR;
}

/* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
for (int i = 0; i < 3; i++) {
uint8_t buf[4];
/* Instruction memories may not support arbitrary read size. Use any size that will work. */
target_addr_t address = (pc - 4) + 4 * i;
*retval = riscv_read_by_any_size(target, address, 4, buf);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
uint32_t value = target_buffer_get_u32(target, buf);
LOG_TARGET_DEBUG(target, "compare 0x%08x from 0x%" PRIx64 " against 0x%08x",
value, address, magic[i]);
if (value != magic[i]) {
LOG_TARGET_DEBUG(target, " -> NONE (no magic)");
return SEMIHOSTING_NONE;
}
if (!sequence_found) {
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (no magic sequence)");
return SEMIHOSTING_NONE;
}

/* Otherwise we have a semihosting call (and semihosting is enabled).
* Proceed with the semihosting. */

/*
* Perform semihosting call if we are not waiting on a fileio
* operation to complete.
Expand All @@ -108,12 +149,14 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
result = riscv_reg_get(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a0)");
return SEMIHOSTING_ERROR;
}

result = riscv_reg_get(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a1)");
return SEMIHOSTING_ERROR;
}

Expand All @@ -128,11 +171,13 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op);
LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (error during semihosting processing)");
return SEMIHOSTING_ERROR;
}
} else {
/* Unknown operation number, not a semihosting call. */
LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op);
LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (unknown semihosting opcode)");
return SEMIHOSTING_NONE;
}
}
Expand All @@ -147,11 +192,11 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
* operation to complete.
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
LOG_TARGET_DEBUG(target, " -> HANDLED");
LOG_TARGET_DEBUG(target, "Semihosting outcome: HANDLED");
return SEMIHOSTING_HANDLED;
}

LOG_TARGET_DEBUG(target, " -> WAITING");
LOG_TARGET_DEBUG(target, "Semihosting outcome: WAITING");
return SEMIHOSTING_WAITING;
}

Expand Down

0 comments on commit 8c0a1cd

Please sign in to comment.