Skip to content

Commit

Permalink
Refine the API in the public header
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
manage 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.

For stdio remapping, rv_remap_stdstream function is introduced.

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

elf is reopened in run_and_trace and dump_test_signature because
elf is allocated inside rv_create and they cannot access them.
It is acceptable to reopen elf since they are only for testing and
debugging. Print inferior exit code to console inside main instead of
syscall_exit because the actual usage of exit code depends on
applications of using riscv public API.

The io interface is not changed in this PR because it could maybe
reused with semu in some way, still need to be investigated. Also,
Logging feature and system emulator integration are not implemented
yet.

related: sysprog21#310
  • Loading branch information
ChinYikMing committed Feb 1, 2024
1 parent 176e121 commit b4b5227
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 192 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
6 changes: 3 additions & 3 deletions src/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ 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 (!PRIV(rv)->allow_misalign && unlikely(addr & (mask_or_pc))), \
if (unlikely(insn_is_misaligned(PC)))) \
{ \
rv->compressed = compress; \
Expand Down Expand Up @@ -1182,15 +1182,15 @@ void ecall_handler(riscv_t *rv)

void memset_handler(riscv_t *rv)
{
memory_t *m = ((state_t *) rv->userdata)->mem;
memory_t *m = PRIV(rv)->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 = PRIV(rv)->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 = PRIV(rv)->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
66 changes: 30 additions & 36 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ 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)
static void run_and_trace(riscv_t *rv, const char *prog_name)
{
elf_t *elf = elf_new();
assert(elf && elf_open(elf, prog_name));
const uint32_t cycles_per_step = 1;

for (; !rv_has_halted(rv);) { /* run until the flag is done */
Expand All @@ -88,15 +90,8 @@ static void run_and_trace(riscv_t *rv, elf_t *elf)
/* 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);
}
elf_delete(elf);
}

static void print_usage(const char *filename)
Expand Down Expand Up @@ -188,8 +183,11 @@ static bool parse_args(int argc, char **args)
return true;
}

static void dump_test_signature(elf_t *elf)
static void dump_test_signature(const char *prog_name)
{
elf_t *elf = elf_new();
assert(elf && elf_open(elf, prog_name));

uint32_t start = 0, end = 0;
const struct Elf32_Sym *sym;
FILE *f = fopen(signature_out_file, "w");
Expand All @@ -212,21 +210,33 @@ static void dump_test_signature(elf_t *elf)
fprintf(f, "%08x\n", memory_read_w(addr));

fclose(f);
elf_delete(elf);
}

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

int main(int argc, char **args)
{
if (argc == 1 || !parse_args(argc, args)) {
print_usage(args[0]);
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 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(attr.emu_data.vm_user);
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 +256,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, &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, opt_prog_name);
}
#if RV32_HAS(GDBSTUB)
else if (opt_gdbstub) {
Expand All @@ -289,14 +284,13 @@ int main(int argc, char **args)

/* dump test result in test mode */
if (opt_arch_test)
dump_test_signature(elf);
dump_test_signature(opt_prog_name);

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

printf("inferior exit code %d\n", attr.exit_code);
return 0;
}
Loading

0 comments on commit b4b5227

Please sign in to comment.