Skip to content

Commit

Permalink
Refine riscv.[ch] public API
Browse files Browse the repository at this point in the history
The following should be included in an emulator's simple and clear
public API:
1. create/init core
2. run emulation
3. delete/destroy core

Other components, including as memory, file systems, program data,
etc., should be abstracted from the user; as a result, setting a
configuration value (vm_attr_t) is sufficient. The user should
concern about memory (state_t) and elf stuff before this PR.
The user may just construct a core, run it, and shut it down
after this PR, so they won't need to worry about them anymore.

The vm_attr_t has multiple fields and they are commented clearly
in the code.

Note that logging feature and system emulator integration are not
implemented yet.

related: sysprog21#310
  • Loading branch information
ChinYikMing committed Jan 31, 2024
1 parent 176e121 commit 9e0542b
Show file tree
Hide file tree
Showing 13 changed files with 321 additions and 221 deletions.
7 changes: 1 addition & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ CFLAGS = -std=gnu99 -O2 -Wall -Wextra
CFLAGS += -Wno-unused-label
CFLAGS += -include src/common.h

# Set the default stack pointer
CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFE000
# Set the default args starting address
CFLAGS += -D DEFAULT_ARGS_ADDR=0xFFFFF000

# Enable link-time optimization (LTO)
ENABLE_LTO ?= 1
ifeq ($(call has, LTO), 1)
Expand Down Expand Up @@ -121,7 +116,7 @@ endif
ENABLE_JIT ?= 0
$(call set-feature, JIT)
ifeq ($(call has, JIT), 1)
OBJS_EXT += jit.o
OBJS_EXT += jit.o
ifneq ($(processor),$(filter $(processor),x86_64 aarch64 arm64))
$(error JIT mode only supports for x64 and arm64 target currently.)
endif
Expand Down
6 changes: 1 addition & 5 deletions src/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,8 @@ bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end)
* Finding data for section headers:
* File start + section_header.offset -> section Data
*/
bool elf_load(elf_t *e, riscv_t *rv, memory_t *mem)
bool elf_load(elf_t *e, memory_t *mem)
{
/* set the entry point */
if (!rv_set_pc(rv, e->hdr->e_entry))
return false;

/* loop over all of the program headers */
for (int p = 0; p < e->hdr->e_phnum; ++p) {
/* find next program header */
Expand Down
2 changes: 1 addition & 1 deletion src/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const char *elf_find_symbol(elf_t *e, uint32_t addr);
bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end);

/* Load the ELF file into a memory abstraction */
bool elf_load(elf_t *e, riscv_t *rv, memory_t *mem);
bool elf_load(elf_t *e, memory_t *mem);

/* get the ELF header */
struct Elf32_Ehdr *get_elf_header(elf_t *e);
Expand Down
7 changes: 4 additions & 3 deletions src/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ RV_EXCEPTION_LIST
*/
#define RV_EXC_MISALIGN_HANDLER(mask_or_pc, type, compress, IO) \
IIF(IO) \
(if (!rv->io.allow_misalign && unlikely(addr & (mask_or_pc))), \
(if (!((vm_attr_t *) rv->userdata)->allow_misalign && \
unlikely(addr & (mask_or_pc))), \
if (unlikely(insn_is_misaligned(PC)))) \
{ \
rv->compressed = compress; \
Expand Down Expand Up @@ -1182,15 +1183,15 @@ void ecall_handler(riscv_t *rv)

void memset_handler(riscv_t *rv)
{
memory_t *m = ((state_t *) rv->userdata)->mem;
memory_t *m = ((vm_attr_t *) rv->userdata)->mem;
memset((char *) m->mem_base + rv->X[rv_reg_a0], rv->X[rv_reg_a1],
rv->X[rv_reg_a2]);
rv->PC = rv->X[rv_reg_ra] & ~1U;
}

void memcpy_handler(riscv_t *rv)
{
memory_t *m = ((state_t *) rv->userdata)->mem;
memory_t *m = ((vm_attr_t *) rv->userdata)->mem;
memcpy((char *) m->mem_base + rv->X[rv_reg_a0],
(char *) m->mem_base + rv->X[rv_reg_a1], rv->X[rv_reg_a2]);
rv->PC = rv->X[rv_reg_ra] & ~1U;
Expand Down
24 changes: 8 additions & 16 deletions src/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,36 @@

static uint8_t *data_memory_base;

/*
* set memory size to 2^32 - 1 bytes
*
* The memory size is set to 2^32 - 1 bytes in order to make this emulator
* portable for both 32-bit and 64-bit platforms. As a result, it can access
* any segment of the memory on either platform. Furthermore, it is safe
* because most of the test cases' data memory usage will not exceed this
* memory size.
*/
#define MEM_SIZE 0xFFFFFFFFULL

memory_t *memory_new(void)
memory_t *memory_new(uint32_t size)
{
if (!size)
return NULL;

memory_t *mem = malloc(sizeof(memory_t));
assert(mem);
#if HAVE_MMAP
data_memory_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
data_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (data_memory_base == MAP_FAILED) {
free(mem);
return NULL;
}
#else
data_memory_base = malloc(MEM_SIZE);
data_memory_base = malloc(size);
if (!data_memory_base) {
free(mem);
return NULL;
}
#endif
mem->mem_base = data_memory_base;
mem->mem_size = MEM_SIZE;
mem->mem_size = size;
return mem;
}

void memory_delete(memory_t *mem)
{
#if HAVE_MMAP
munmap(mem->mem_base, MEM_SIZE);
munmap(mem->mem_base, mem->mem_size);
#else
free(mem->mem_base);
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ typedef struct {
uint64_t mem_size;
} memory_t;

memory_t *memory_new(void);
memory_t *memory_new(uint32_t size);
void memory_delete(memory_t *m);

/* read a C-style string from memory */
Expand Down
4 changes: 2 additions & 2 deletions src/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ static void do_fuse2(struct jit_state *state, riscv_t *rv UNUSED, rv_insn_t *ir)

static void do_fuse3(struct jit_state *state, riscv_t *rv, rv_insn_t *ir)
{
memory_t *m = ((state_t *) rv->userdata)->mem;
memory_t *m = ((vm_attr_t *) rv->userdata)->mem;
opcode_fuse_t *fuse = ir->fuse;
for (int i = 0; i < ir->imm2; i++) {
emit_load(state, S32, parameter_reg[0], temp_reg[0],
Expand All @@ -1300,7 +1300,7 @@ static void do_fuse3(struct jit_state *state, riscv_t *rv, rv_insn_t *ir)

static void do_fuse4(struct jit_state *state, riscv_t *rv, rv_insn_t *ir)
{
memory_t *m = ((state_t *) rv->userdata)->mem;
memory_t *m = ((vm_attr_t *) rv->userdata)->mem;
opcode_fuse_t *fuse = ir->fuse;
for (int i = 0; i < ir->imm2; i++) {
emit_load(state, S32, parameter_reg[0], temp_reg[0],
Expand Down
137 changes: 61 additions & 76 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <string.h>
#include <unistd.h>

#include "elf.h"
#include "riscv.h"
#include "utils.h"

Expand Down Expand Up @@ -75,29 +74,20 @@ IO_HANDLER_IMPL(byte, write_b, W)
#undef W

/* run: printing out an instruction trace */
static void run_and_trace(riscv_t *rv, elf_t *elf)
{
const uint32_t cycles_per_step = 1;

for (; !rv_has_halted(rv);) { /* run until the flag is done */
/* trace execution */
uint32_t pc = rv_get_pc(rv);
const char *sym = elf_find_symbol(elf, pc);
printf("%08x %s\n", pc, (sym ? sym : ""));

/* step instructions */
rv_step(rv, cycles_per_step);
}
}

static void run(riscv_t *rv)
{
const uint32_t cycles_per_step = 100;
for (; !rv_has_halted(rv);) { /* run until the flag is done */
/* step instructions */
rv_step(rv, cycles_per_step);
}
}
// static void run_and_trace(riscv_t *rv)
//{
// const uint32_t cycles_per_step = 1;
//
// for (; !rv_has_halted(rv);) { /* run until the flag is done */
// /* trace execution */
// uint32_t pc = rv_get_pc(rv);
// const char *sym = elf_find_symbol(elf, pc);
// printf("%08x %s\n", pc, (sym ? sym : ""));
//
// /* step instructions */
// rv_step(rv, cycles_per_step);
// }
// }

static void print_usage(const char *filename)
{
Expand Down Expand Up @@ -188,31 +178,35 @@ static bool parse_args(int argc, char **args)
return true;
}

static void dump_test_signature(elf_t *elf)
{
uint32_t start = 0, end = 0;
const struct Elf32_Sym *sym;
FILE *f = fopen(signature_out_file, "w");
if (!f) {
fprintf(stderr, "Cannot open signature output file.\n");
return;
}

/* use the entire .data section as a fallback */
elf_get_data_section_range(elf, &start, &end);

/* try and access the exact signature range */
if ((sym = elf_get_symbol(elf, "begin_signature")))
start = sym->st_value;
if ((sym = elf_get_symbol(elf, "end_signature")))
end = sym->st_value;

/* dump it word by word */
for (uint32_t addr = start; addr < end; addr += 4)
fprintf(f, "%08x\n", memory_read_w(addr));

fclose(f);
}
// static void dump_test_signature(elf_t *elf)
//{
// uint32_t start = 0, end = 0;
// const struct Elf32_Sym *sym;
// FILE *f = fopen(signature_out_file, "w");
// if (!f) {
// fprintf(stderr, "Cannot open signature output file.\n");
// return;
// }
//
// /* use the entire .data section as a fallback */
// elf_get_data_section_range(elf, &start, &end);
//
// /* try and access the exact signature range */
// if ((sym = elf_get_symbol(elf, "begin_signature")))
// start = sym->st_value;
// if ((sym = elf_get_symbol(elf, "end_signature")))
// end = sym->st_value;
//
// /* dump it word by word */
// for (uint32_t addr = start; addr < end; addr += 4)
// fprintf(f, "%08x\n", memory_read_w(addr));
//
// fclose(f);
// }

#define MEM_SIZE 0xFFFFFFFFULL /* 2^32 - 1 */
#define STACK_SIZE 0x1000 /* 4096 */
#define ARGS_OFFSET_SIZE 0x1000 /* 4096 */

int main(int argc, char **args)
{
Expand All @@ -221,12 +215,19 @@ int main(int argc, char **args)
return 1;
}

/* open the ELF file from the file system */
elf_t *elf = elf_new();
if (!elf_open(elf, opt_prog_name)) {
fprintf(stderr, "Unable to open ELF file '%s'\n", opt_prog_name);
return 1;
}
vm_attr_t vm_attr = {
.mem_size = MEM_SIZE,
.stack_size = STACK_SIZE,
.args_offset_size = ARGS_OFFSET_SIZE,
.argc = prog_argc,
.argv = prog_args,
.logging_level = 0,
.emu_data.vm_user = malloc(sizeof(vm_user_t)),
.cycle_per_step = 100,
.allow_misalign = opt_misaligned,
};
assert(vm_attr.emu_data.vm_user);
vm_attr.emu_data.vm_user->elf_program = opt_prog_name;

/* install the I/O handlers for the RISC-V runtime */
const riscv_io_t io = {
Expand All @@ -246,33 +247,18 @@ int main(int argc, char **args)
.on_ebreak = ebreak_handler,
.on_memcpy = memcpy_handler,
.on_memset = memset_handler,
.allow_misalign = opt_misaligned,
};

state_t *state = state_new();

/* find the start of the heap */
const struct Elf32_Sym *end;
if ((end = elf_get_symbol(elf, "_end")))
state->break_addr = end->st_value;

/* create the RISC-V runtime */
riscv_t *rv =
rv_create(&io, state, prog_argc, prog_args, !opt_quiet_outputs);
riscv_t *rv = rv_create(&io, &vm_attr);
if (!rv) {
fprintf(stderr, "Unable to create riscv emulator\n");
return 1;
}

/* load the ELF file into the memory abstraction */
if (!elf_load(elf, rv, state->mem)) {
fprintf(stderr, "Unable to load ELF file '%s'\n", args[1]);
return 1;
}

/* run based on the specified mode */
if (opt_trace) {
run_and_trace(rv, elf);
/* run_and_trace(rv); */
}
#if RV32_HAS(GDBSTUB)
else if (opt_gdbstub) {
Expand All @@ -288,15 +274,14 @@ int main(int argc, char **args)
dump_registers(rv, registers_out_file);

/* dump test result in test mode */
if (opt_arch_test)
dump_test_signature(elf);
if (opt_arch_test) {
/* dump_test_signature(vm_attr->elf); */
}

if (opt_prof_data)
rv_profile(rv, prof_out_file);
/* finalize the RISC-V runtime */
elf_delete(elf);
rv_delete(rv);
state_delete(state);

return 0;
}
Loading

0 comments on commit 9e0542b

Please sign in to comment.