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_t is reopened in run_and_trace and dump_test_signature because
elf_t is allocated inside rv_create and they cannot access them.
It is acceptable to reopen elf_t since they are only for testing and
debugging.

PRINT_EXIT_CODE build macro is introduced to enable syscall_exit
to print exit code to console only during testing since the actual usage
of exit code is really depending on applications.

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 fde9ce2
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 191 deletions.
9 changes: 3 additions & 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 Expand Up @@ -202,6 +197,7 @@ EXPECTED_puzzle = success in 2005 trials
EXPECTED_fcalc = Performed 12 tests, 0 failures, 100% success rate.
EXPECTED_pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086

check: CFLAGS += -DPRINT_EXIT_CODE
check: $(BIN)
$(Q)$(foreach e,$(CHECK_ELF_FILES),\
$(PRINTF) "Running $(e).elf ... "; \
Expand All @@ -214,6 +210,7 @@ check: $(BIN)
)

EXPECTED_aes_sha1 = 1242a6757c8aef23e50b5264f5941a2f4b4a347e -
misalign: CFLAGS += -DPRINT_EXIT_CODE
misalign: $(BIN)
$(Q)$(PRINTF) "Running aes.elf ... ";
$(Q)if [ "$(shell $(BIN) -m $(OUT)/aes.elf | $(SHA1SUM))" = "$(EXPECTED_aes_sha1)" ]; then \
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
67 changes: 31 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 @@ -249,30 +259,16 @@ int main(int argc, char **args)
.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 @@ -288,15 +284,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(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);

return 0;
}
Loading

0 comments on commit fde9ce2

Please sign in to comment.