unicorn/qemu/target/riscv/unicorn.c
Lioncash b6f752970b
target/riscv: Initial introduction of the RISC-V target
This ports over the RISC-V architecture from Qemu. This is currently a
very barebones transition. No code hooking or any fancy stuff.
Currently, you can feed it instructions and query the CPU state itself.

This also allows choosing whether or not RISC-V 32-bit or RISC-V 64-bit
is desirable through Unicorn's interface as well.

Extremely basic examples of executing a single instruction have been
added to the samples directory to help demonstrate how to use the basic
functionality.
2019-03-08 21:46:10 -05:00

118 lines
3.4 KiB
C

/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#include <string.h>
#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/boards.h"
#include "hw/riscv/spike.h"
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "unicorn_common.h"
#include "uc_priv.h"
#ifdef TARGET_RISCV32
const int RISCV32_REGS_STORAGE_SIZE = offsetof(CPURISCVState, tlb_table);
#else
const int RISCV64_REGS_STORAGE_SIZE = offsetof(CPURISCVState, tlb_table);
#endif
static void riscv_release(void *ctx) {
TCGContext *tcg_ctx = (TCGContext *) ctx;
release_common(ctx);
g_free(tcg_ctx->tb_ctx.tbs);
}
static void riscv_reg_reset(struct uc_struct *uc) {
CPUArchState *env = uc->cpu->env_ptr;
memset(env->gpr, 0, sizeof(env->gpr));
memset(env->fpr, 0, sizeof(env->fpr));
env->priv = PRV_M;
env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
env->mcause = 0;
env->pc = env->resetvec;
set_default_nan_mode(1, &env->fp_status);
}
static int riscv_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int count) {
CPUState *const cs = uc->cpu;
CPURISCVState *const state = &RISCV_CPU(uc, cs)->env;
for (int i = 0; i < count; i++) {
const unsigned int reg_id = regs[i];
void *const value = vals[i];
if (reg_id >= UC_RISCV_REG_X0 && reg_id <= UC_RISCV_REG_X31) {
memcpy(value, &state->gpr[reg_id - UC_RISCV_REG_X0], sizeof(state->gpr[0]));
} else if (reg_id >= UC_RISCV_REG_F0 && reg_id <= UC_RISCV_REG_F31) {
memcpy(value, &state->fpr[reg_id - UC_RISCV_REG_F0], sizeof(state->fpr[0]));
} else if (reg_id == UC_RISCV_REG_PC) {
memcpy(value, &state->pc, sizeof(state->pc));
}
}
return 0;
}
static int riscv_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, int count) {
CPUState *const cs = uc->cpu;
CPURISCVState *const state = &RISCV_CPU(uc, cs)->env;
for (int i = 0; i < count; i++) {
const unsigned int reg_id = regs[i];
const void *value = vals[i];
// Intentionally exclude the zero register (X0) in the lower-bound
if (reg_id > UC_RISCV_REG_X0 && reg_id <= UC_RISCV_REG_X31) {
memcpy(&state->gpr[reg_id - UC_RISCV_REG_X0], value, sizeof(state->gpr[0]));
} else if (reg_id >= UC_RISCV_REG_F0 && reg_id <= UC_RISCV_REG_F31) {
memcpy(&state->fpr[reg_id - UC_RISCV_REG_F0], value, sizeof(state->fpr[0]));
} else if (reg_id == UC_RISCV_REG_PC) {
memcpy(&state->pc, value, sizeof(state->pc));
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
}
}
return 0;
}
static void riscv_set_pc(struct uc_struct *uc, uint64_t address) {
CPURISCVState *state = uc->cpu->env_ptr;
state->pc = address;
}
static bool riscv_stop_interrupt(int int_no) {
switch(int_no) {
default:
return false;
}
}
DEFAULT_VISIBILITY
#ifdef TARGET_RISCV32
void riscv32_uc_init(struct uc_struct *uc) {
#else
void riscv64_uc_init(struct uc_struct *uc) {
#endif
register_accel_types(uc);
riscv_cpu_register_types(uc);
spike_v1_10_0_machine_init_register_types(uc);
uc->release = riscv_release;
uc->reg_read = riscv_reg_read;
uc->reg_write = riscv_reg_write;
uc->reg_reset = riscv_reg_reset;
uc->set_pc = riscv_set_pc;
uc->stop_interrupt = riscv_stop_interrupt;
uc_common_init(uc);
}