diff --git a/Makefile b/Makefile index cda3c502..c3641968 100644 --- a/Makefile +++ b/Makefile @@ -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) @@ -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 diff --git a/src/elf.c b/src/elf.c index 7d441402..4f3f4e48 100644 --- a/src/elf.c +++ b/src/elf.c @@ -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 */ diff --git a/src/elf.h b/src/elf.h index e43caf9a..6ebb8072 100644 --- a/src/elf.h +++ b/src/elf.h @@ -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); diff --git a/src/emulate.c b/src/emulate.c index ada47350..dd3886ce 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -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; \ @@ -1182,7 +1182,7 @@ 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; @@ -1190,7 +1190,7 @@ void memset_handler(riscv_t *rv) 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; diff --git a/src/io.c b/src/io.c index ce66ce46..6da5d284 100644 --- a/src/io.c +++ b/src/io.c @@ -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 diff --git a/src/io.h b/src/io.h index cc93008f..9a674179 100644 --- a/src/io.h +++ b/src/io.h @@ -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 */ diff --git a/src/jit.c b/src/jit.c index a24648da..5af3155f 100644 --- a/src/jit.c +++ b/src/jit.c @@ -39,6 +39,7 @@ #include "io.h" #include "jit.h" #include "riscv.h" +#include "riscv_private.h" #include "utils.h" #define JIT_CLS_MASK 0x07 @@ -1284,7 +1285,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], @@ -1300,7 +1301,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 = 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], diff --git a/src/main.c b/src/main.c index 71d900b7..12ecc51b 100644 --- a/src/main.c +++ b/src/main.c @@ -74,35 +74,10 @@ IO_HANDLER_IMPL(byte, write_b, W) #undef R #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 print_usage(const char *filename) { fprintf(stderr, - "RV32I[MA] Emulator which loads an ELF file to execute.\n" + "RV32I[MACF] Emulator which loads an ELF file to execute.\n" "Usage: %s [options] [filename] [arguments]\n" "Options:\n" " -t : print executable trace\n" @@ -188,8 +163,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"); @@ -212,8 +190,13 @@ 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)) { @@ -221,12 +204,28 @@ 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; - } + int run_flag = 0; + run_flag |= opt_trace; +#if RV32_HAS(GDBSTUB) + run_flag |= opt_gdbstub << 1; +#endif + run_flag |= opt_prof_data << 2; + + vm_attr_t attr = { + .mem_size = MEM_SIZE, + .stack_size = STACK_SIZE, + .args_offset_size = ARGS_OFFSET_SIZE, + .argc = prog_argc, + .argv = prog_args, + .log_level = 0, + .run_flag = run_flag, + .profile_output_file = prof_out_file, + .data.user = malloc(sizeof(vm_user_t)), + .cycle_per_step = 100, + .allow_misalign = opt_misaligned, + }; + assert(attr.data.user); + attr.data.user->elf_program = opt_prog_name; /* install the I/O handlers for the RISC-V runtime */ const riscv_io_t io = { @@ -246,42 +245,16 @@ 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); - } -#if RV32_HAS(GDBSTUB) - else if (opt_gdbstub) { - rv_debug(rv); - } -#endif - else { - run(rv); - } + rv_run(rv); /* dump registers as JSON */ if (opt_dump_regs) @@ -289,14 +262,11 @@ 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; } diff --git a/src/riscv.c b/src/riscv.c index 1399ff6b..e68a2648 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -8,6 +8,17 @@ #include #include +#if !defined(_WIN32) && !defined(_WIN64) +#include +#define FILENO(x) fileno(x) +#else +#define FILENO(x) _fileno(x) +#define STDIN_FILENO FILENO(stdin) +#define STDOUT_FILENO FILENO(stdout) +#define STDERR_FILENO FILENO(stderr) +#endif + +#include "elf.h" #include "mpool.h" #include "riscv.h" #include "riscv_private.h" @@ -63,12 +74,6 @@ static void block_map_destroy(riscv_t *rv) } #endif -riscv_user_t rv_userdata(riscv_t *rv) -{ - assert(rv); - return rv->userdata; -} - bool rv_set_pc(riscv_t *rv, riscv_word_t pc) { assert(rv); @@ -105,13 +110,55 @@ riscv_word_t rv_get_reg(riscv_t *rv, uint32_t reg) return ~0U; } -riscv_t *rv_create(const riscv_io_t *io, - riscv_user_t userdata, - int argc, - char **args, - bool output_exit_code) +/* + * Remap standard stream + * + * @rv: riscv + * @fsp: a list of pair of mapping from fd to FILE * + * @fsp_size: list size + * + * Note: fd inside fsp should be 0 or 1 or 2 only + * + */ +void rv_remap_stdstream(riscv_t *rv, fd_stream_pair_t *fsp, uint32_t fsp_size) +{ + assert(rv); + + vm_attr_t *attr = PRIV(rv); + assert(attr && attr->fd_map); + + for (uint32_t i = 0; i < fsp_size; i++) { + int fd = fsp[i].fd; + FILE *file = fsp[i].file; + + if (!file) + continue; + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + continue; + + /* check if standard stream refered by fd exists or not */ + map_iter_t it; + map_find(attr->fd_map, &it, &fd); + if (it.node) /* found, remove first */ + map_erase(attr->fd_map, &it); + map_insert(attr->fd_map, &fd, &file); + + /* store new fd to make the vm_attr_t consistent */ + int new_fd = FILENO(file); + assert(new_fd != -1); + + if (fd == STDIN_FILENO) + attr->fd_stdin = new_fd; + else if (fd == STDOUT_FILENO) + attr->fd_stdout = new_fd; + else + attr->fd_stderr = new_fd; + } +} + +riscv_t *rv_create(const riscv_io_t *io, riscv_user_t rv_attr) { - assert(io); + assert(io && rv_attr); riscv_t *rv = calloc(1, sizeof(riscv_t)); assert(rv); @@ -119,10 +166,48 @@ riscv_t *rv_create(const riscv_io_t *io, /* copy over the IO interface */ memcpy(&rv->io, io, sizeof(riscv_io_t)); - /* copy over the userdata */ - rv->userdata = userdata; + /* copy over the attr */ + rv->data = rv_attr; + + vm_attr_t *attr = PRIV(rv); + attr->mem = memory_new(attr->mem_size); + assert(attr->mem); + + /* reset */ + rv_reset(rv, 0U); + + if (attr->data.user) { + elf_t *elf = elf_new(); + assert(elf && elf_open(elf, (attr->data.user)->elf_program)); + + const struct Elf32_Sym *end; + if ((end = elf_get_symbol(elf, "_end"))) + attr->break_addr = end->st_value; + + assert(elf_load(elf, attr->mem)); - rv->output_exit_code = output_exit_code; + /* set the entry pc */ + const struct Elf32_Ehdr *hdr = get_elf_header(elf); + assert(rv_set_pc(rv, hdr->e_entry)); + + elf_delete(elf); + } else { + /* TODO: system emulator */ + } + + /* + * default standard stream + * + * rv_remap_stdstream can be called to overwrite them + */ + attr->fd_map = map_init(int, FILE *, map_cmp_int); + rv_remap_stdstream(rv, + (fd_stream_pair_t[]){ + {STDIN_FILENO, stdin}, + {STDOUT_FILENO, stdout}, + {STDERR_FILENO, stderr}, + }, + 3); /* create block and IRs memory pool */ rv->block_mp = mpool_create(sizeof(block_t) << BLOCK_MAP_CAPACITY_BITS, @@ -141,12 +226,65 @@ riscv_t *rv_create(const riscv_io_t *io, rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS); assert(rv->block_cache); #endif - /* reset */ - rv_reset(rv, 0U, argc, args); return rv; } +static void rv_run_and_trace(riscv_t *rv) +{ + assert(rv); + + vm_attr_t *attr = PRIV(rv); + assert(attr && attr->data.user && attr->data.user->elf_program); + + const char *prog_name = attr->data.user->elf_program; + 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 */ + /* trace execution */ + uint32_t pc = rv_get_pc(rv); + const char *sym = elf_find_symbol(elf, pc); + printf("%08x %s\n", pc, (sym ? sym : "")); + + rv_step(rv, cycles_per_step); /* step instructions */ + } + + elf_delete(elf); +} + +#if RV32_HAS(GDBSTUB) +/* Run the RISC-V emulator as gdbstub */ +void rv_debug(riscv_t *rv); +#endif + +void rv_profile(riscv_t *rv, char *out_file_path); + +void rv_run(riscv_t *rv) +{ + assert(rv); + + vm_attr_t *attr = PRIV(rv); + assert(attr && attr->data.user && attr->data.user->elf_program); + + if (attr->run_flag & RV_RUN_TRACE) + rv_run_and_trace(rv); +#if RV32_HAS(GDBSTUB) + else if (attr->run_flag & RV_RUN_GDBSTUB) + rv_debug(rv); +#endif + else { /* default main loop */ + for (; !rv_has_halted(rv);) /* run until the flag is done */ + rv_step(rv, attr->cycle_per_step); /* step instructions */ + } + + if (attr->run_flag & RV_RUN_PROFILE) { + assert(attr->profile_output_file); + rv_profile(rv, attr->profile_output_file); + } +} + void rv_halt(riscv_t *rv) { rv->halt = true; @@ -157,15 +295,13 @@ bool rv_has_halted(riscv_t *rv) return rv->halt; } -bool rv_enables_to_output_exit_code(riscv_t *rv) -{ - return rv->output_exit_code; -} - void rv_delete(riscv_t *rv) { assert(rv); #if !RV32_HAS(JIT) + vm_attr_t *attr = PRIV(rv); + map_delete(attr->fd_map); + memory_delete(attr->mem); block_map_destroy(rv); #else mpool_destroy(rv->chain_entry_mp); @@ -175,16 +311,22 @@ void rv_delete(riscv_t *rv) free(rv); } -void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) +void rv_reset(riscv_t *rv, riscv_word_t pc) { assert(rv); memset(rv->X, 0, sizeof(uint32_t) * N_RV_REGS); + vm_attr_t *attr = PRIV(rv); + int argc = attr->argc; + char **args = attr->argv; + memory_t *mem = attr->mem; + /* set the reset address */ rv->PC = pc; /* set the default stack pointer */ - rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR; + rv->X[rv_reg_sp] = + attr->mem_size - attr->stack_size - attr->args_offset_size; /* Store 'argc' and 'args' of the target program in 'state->mem'. Thus, * we can use an offset trick to emulate 32/64-bit target programs on @@ -218,17 +360,15 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) * TODO: access to envp */ - state_t *s = rv_userdata(rv); - /* copy args to RAM */ uintptr_t args_size = (1 + argc + 1) * sizeof(uint32_t); - uintptr_t args_bottom = DEFAULT_ARGS_ADDR; + uintptr_t args_bottom = attr->mem_size - attr->stack_size; uintptr_t args_top = args_bottom - args_size; args_top &= 16; /* argc */ uintptr_t *args_p = (uintptr_t *) args_top; - memory_write(s->mem, (uintptr_t) args_p, (void *) &argc, sizeof(int)); + memory_write(mem, (uintptr_t) args_p, (void *) &argc, sizeof(int)); args_p++; /* args */ @@ -240,7 +380,7 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) for (int i = 0; i < argc; i++) { const char *arg = args[i]; args_len = strlen(arg); - memory_write(s->mem, (uintptr_t) args_p, (void *) arg, + memory_write(mem, (uintptr_t) args_p, (void *) arg, (args_len + 1) * sizeof(uint8_t)); args_space[args_space_idx++] = args_len + 1; args_p = (uintptr_t *) ((uintptr_t) args_p + args_len + 1); @@ -257,8 +397,8 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) /* argc */ uintptr_t *sp = (uintptr_t *) stack_top; - memory_write(s->mem, (uintptr_t) sp, - (void *) (s->mem->mem_base + (uintptr_t) args_p), sizeof(int)); + memory_write(mem, (uintptr_t) sp, + (void *) (mem->mem_base + (uintptr_t) args_p), sizeof(int)); args_p++; /* keep argc and args[0] within one word due to RV32 ABI */ sp = (uintptr_t *) ((uint32_t *) sp + 1); @@ -266,12 +406,11 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) /* args */ for (int i = 0; i < argc; i++) { uintptr_t offset = (uintptr_t) args_p; - memory_write(s->mem, (uintptr_t) sp, (void *) &offset, - sizeof(uintptr_t)); + memory_write(mem, (uintptr_t) sp, (void *) &offset, sizeof(uintptr_t)); args_p = (uintptr_t *) ((uintptr_t) args_p + args_space[i]); sp = (uintptr_t *) ((uint32_t *) sp + 1); } - memory_fill(s->mem, (uintptr_t) sp, sizeof(uint32_t), 0); + memory_fill(mem, (uintptr_t) sp, sizeof(uint32_t), 0); /* reset sp pointing to argc */ rv->X[rv_reg_sp] = stack_top; @@ -301,31 +440,6 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) rv->halt = false; } -state_t *state_new(void) -{ - state_t *s = malloc(sizeof(state_t)); - assert(s); - s->mem = memory_new(); - s->break_addr = 0; - - s->fd_map = map_init(int, FILE *, map_cmp_int); - FILE *files[] = {stdin, stdout, stderr}; - for (size_t i = 0; i < ARRAYS_SIZE(files); i++) - map_insert(s->fd_map, &i, &files[i]); - - return s; -} - -void state_delete(state_t *s) -{ - if (!s) - return; - - map_delete(s->fd_map); - memory_delete(s->mem); - free(s); -} - static const char *insn_name_table[] = { #define _(inst, can_branch, insn_len, translatable, reg_mask) \ [rv_insn_##inst] = #inst, diff --git a/src/riscv.h b/src/riscv.h index b109a12a..c4041b87 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -7,6 +7,7 @@ #include #include +#include #include "io.h" #include "map.h" @@ -143,22 +144,19 @@ typedef struct { riscv_on_ebreak on_ebreak; riscv_on_memset on_memset; riscv_on_memcpy on_memcpy; - /* enable misaligned memory access */ - bool allow_misalign; } riscv_io_t; +/* run emulation */ +void rv_run(riscv_t *rv); + /* create a RISC-V emulator */ -riscv_t *rv_create(const riscv_io_t *io, - riscv_user_t user_data, - int argc, - char **args, - bool output_exit_code); +riscv_t *rv_create(const riscv_io_t *io, riscv_user_t attr); /* delete a RISC-V emulator */ void rv_delete(riscv_t *rv); /* reset the RISC-V processor */ -void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args); +void rv_reset(riscv_t *rv, riscv_word_t pc); #if RV32_HAS(GDBSTUB) /* Run the RISC-V emulator as gdbstub */ @@ -168,9 +166,6 @@ void rv_debug(riscv_t *rv); /* step the RISC-V emulator */ void rv_step(riscv_t *rv, int32_t cycles); -/* get RISC-V user data bound to an emulator */ -riscv_user_t rv_userdata(riscv_t *rv); - /* set the program counter of a RISC-V emulator */ bool rv_set_pc(riscv_t *rv, riscv_word_t pc); @@ -180,6 +175,14 @@ riscv_word_t rv_get_pc(riscv_t *rv); /* set a register of the RISC-V emulator */ void rv_set_reg(riscv_t *rv, uint32_t reg, riscv_word_t in); +typedef struct { + int fd; + FILE *file; +} fd_stream_pair_t; + +/* remap standard stream to other stream */ +void rv_remap_stdstream(riscv_t *rv, fd_stream_pair_t *fsp, uint32_t fsp_size); + /* get a register of the RISC-V emulator */ riscv_word_t rv_get_reg(riscv_t *rv, uint32_t reg); @@ -207,27 +210,97 @@ void rv_halt(riscv_t *rv); /* return the halt state */ bool rv_has_halted(riscv_t *rv); -/* return the flag of outputting exit code */ -bool rv_enables_to_output_exit_code(riscv_t *rv); +enum { + /* run and trace instructions and print them out during emulation */ + RV_RUN_TRACE = 1, + + /* run as gdbstub during emulation */ + RV_RUN_GDBSTUB = 2, + + /* run and profile relationship of blocks and save to prof_output_file + during emulation */ + RV_RUN_PROFILE = 4, +}; -/* state structure passed to the runtime */ typedef struct { + char *elf_program; +} vm_user_t; + +typedef union { + vm_user_t *user; + /* TODO: system emulator stuff */ +} vm_data_t; + +typedef struct { + /* vm memory object */ memory_t *mem; - /* the data segment break address */ - riscv_word_t break_addr; + /* + * max memory size is 2^32 - 1 bytes + * + * it is for portable on both 32-bit and 64-bit platforms. In this way, + * emulator can access any segment of the memory on either platform. + */ + uint32_t mem_size; - /* file descriptor map: int -> (FILE *) */ - map_t fd_map; -} state_t; + /* vm main stack size */ + uint32_t stack_size; + + /* + * To deal with the RV32 ABI for accessing args list, + * offset of args data have to be saved. + * + * args_offset_size is the memory size to store the offset + */ + uint32_t args_offset_size; + + /* arguments of emulation program */ + int argc; + char **argv; + /* FIXME: rv32emu cannot access envp yet */ + + /* emulation program exit code */ + int exit_code; + + /* emulation program error code */ + int error; + + /* TODO: for logging feature */ + int log_level; -/* create a state */ -state_t *state_new(void); + /* userspace or system emulation data */ + vm_data_t data; -/* delete a state */ -void state_delete(state_t *s); + /* number of cycle(instruction) in a rv_step call*/ + int cycle_per_step; -void rv_profile(riscv_t *rv, char *out_file_path); + /* allow misaligned memory access */ + bool allow_misalign; + + /* + * run flag, it is the bitwise OR from + * RV_RUN_TRACE, RV_RUN_GDBSTUB, and RV_RUN_PROFILE + */ + uint8_t run_flag; + + /* profiling output file if RV_RUN_PROFILE is set in run_flag */ + char *profile_output_file; + + /* + * set by rv_create during initialization + * + * use rv_remap_stdstream to overwrite them + */ + int fd_stdin; + int fd_stdout; + int fd_stderr; + + /* vm file descriptor map: int -> (FILE *) */ + map_t fd_map; + + /* the data segment break address */ + riscv_word_t break_addr; +} vm_attr_t; #ifdef __cplusplus }; diff --git a/src/riscv_private.h b/src/riscv_private.h index 0ec29bd4..ddae6f1a 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -17,6 +17,8 @@ #include "cache.h" #endif +#define PRIV(x) ((vm_attr_t *) x->data) + /* CSRs */ enum { /* floating point */ @@ -100,7 +102,7 @@ struct riscv_internal { riscv_word_t PC; /* user provided data */ - riscv_user_t userdata; + riscv_user_t data; #if RV32_HAS(EXT_F) /* float registers */ @@ -129,8 +131,7 @@ struct riscv_internal { struct mpool *chain_entry_mp; #endif struct mpool *block_mp, *block_ir_mp; - /* print exit code on syscall_exit */ - bool output_exit_code; + void *jit_state; #if RV32_HAS(GDBSTUB) /* gdbstub instance */ diff --git a/src/syscall.c b/src/syscall.c index 8c2e867f..98dc2545 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -10,6 +10,7 @@ #include #include "riscv.h" +#include "riscv_private.h" #include "utils.h" #define PREALLOC_SIZE 4096 @@ -53,12 +54,12 @@ enum { O_ACCMODE = 3, }; -static int find_free_fd(state_t *s) +static int find_free_fd(vm_attr_t *attr) { for (int i = 3;; ++i) { map_iter_t it; - map_find(s->fd_map, &it, &i); - if (map_at_end(s->fd_map, &it)) + map_find(attr->fd_map, &it, &i); + if (map_at_end(attr->fd_map, &it)) return i; } } @@ -80,7 +81,7 @@ static const char *get_mode_str(uint32_t flags, uint32_t mode UNUSED) static uint8_t tmp[PREALLOC_SIZE]; static void syscall_write(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* _write(fde, buffer, count) */ riscv_word_t fd = rv_get_reg(rv, rv_reg_a0); @@ -89,12 +90,12 @@ static void syscall_write(riscv_t *rv) /* lookup the file descriptor */ map_iter_t it; - map_find(s->fd_map, &it, &fd); + map_find(attr->fd_map, &it, &fd); uint32_t total_write = 0; while (count > PREALLOC_SIZE) { - memory_read(s->mem, tmp, buffer + total_write, PREALLOC_SIZE); - if (!map_at_end(s->fd_map, &it)) { + memory_read(attr->mem, tmp, buffer + total_write, PREALLOC_SIZE); + if (!map_at_end(attr->fd_map, &it)) { /* write out the data */ FILE *handle = map_iter_value(&it, FILE *); size_t written = fwrite(tmp, 1, PREALLOC_SIZE, handle); @@ -105,8 +106,8 @@ static void syscall_write(riscv_t *rv) } else goto error_handler; } - memory_read(s->mem, tmp, buffer + total_write, count); - if (!map_at_end(s->fd_map, &it)) { + memory_read(attr->mem, tmp, buffer + total_write, count); + if (!map_at_end(attr->fd_map, &it)) { /* write out the data */ FILE *handle = map_iter_value(&it, FILE *); size_t written = fwrite(tmp, 1, count, handle); @@ -128,13 +129,15 @@ static void syscall_write(riscv_t *rv) static void syscall_exit(riscv_t *rv) { + /* + * simply halt cpu and save exit code + * + * the application decides the usage of exit code + */ rv_halt(rv); - /* To avoid mixing with JSON output */ - if (rv_enables_to_output_exit_code(rv)) { - riscv_word_t code = rv_get_reg(rv, rv_reg_a0); - fprintf(stdout, "inferior exit code %d\n", (int) code); - } + vm_attr_t *attr = PRIV(rv); + attr->exit_code = rv_get_reg(rv, rv_reg_a0); } /* brk(increment) @@ -144,15 +147,15 @@ static void syscall_exit(riscv_t *rv) */ static void syscall_brk(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* get the increment parameter */ riscv_word_t increment = rv_get_reg(rv, rv_reg_a0); if (increment) - s->break_addr = increment; + attr->break_addr = increment; /* return new break address */ - rv_set_reg(rv, rv_reg_a0, s->break_addr); + rv_set_reg(rv, rv_reg_a0, attr->break_addr); } static void syscall_gettimeofday(riscv_t *rv) @@ -207,21 +210,21 @@ static void syscall_clock_gettime(riscv_t *rv) static void syscall_close(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* _close(fd); */ uint32_t fd = rv_get_reg(rv, rv_reg_a0); if (fd >= 3) { /* lookup the file descriptor */ map_iter_t it; - map_find(s->fd_map, &it, &fd); - if (!map_at_end(s->fd_map, &it)) { + map_find(attr->fd_map, &it, &fd); + if (!map_at_end(attr->fd_map, &it)) { if (fclose(map_iter_value(&it, FILE *))) { /* error */ rv_set_reg(rv, rv_reg_a0, -1); return; } - map_erase(s->fd_map, &it); + map_erase(attr->fd_map, &it); /* success */ rv_set_reg(rv, rv_reg_a0, 0); @@ -238,7 +241,7 @@ static void syscall_close(riscv_t *rv) */ static void syscall_lseek(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* _lseek(fd, offset, whence); */ uint32_t fd = rv_get_reg(rv, rv_reg_a0); @@ -247,8 +250,8 @@ static void syscall_lseek(riscv_t *rv) /* find the file descriptor */ map_iter_t it; - map_find(s->fd_map, &it, &fd); - if (map_at_end(s->fd_map, &it)) { + map_find(attr->fd_map, &it, &fd); + if (map_at_end(attr->fd_map, &it)) { /* error */ rv_set_reg(rv, rv_reg_a0, -1); return; @@ -267,7 +270,7 @@ static void syscall_lseek(riscv_t *rv) static void syscall_read(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* _read(fd, buf, count); */ uint32_t fd = rv_get_reg(rv, rv_reg_a0); @@ -276,8 +279,8 @@ static void syscall_read(riscv_t *rv) /* lookup the file */ map_iter_t it; - map_find(s->fd_map, &it, &fd); - if (map_at_end(s->fd_map, &it)) { + map_find(attr->fd_map, &it, &fd); + if (map_at_end(attr->fd_map, &it)) { /* error */ rv_set_reg(rv, rv_reg_a0, -1); return; @@ -289,14 +292,14 @@ static void syscall_read(riscv_t *rv) while (count > PREALLOC_SIZE) { size_t r = fread(tmp, 1, PREALLOC_SIZE, handle); - memory_write(s->mem, buf + total_read, tmp, r); + memory_write(attr->mem, buf + total_read, tmp, r); count -= r; total_read += r; if (r != PREALLOC_SIZE) break; } size_t r = fread(tmp, 1, count, handle); - memory_write(s->mem, buf + total_read, tmp, r); + memory_write(attr->mem, buf + total_read, tmp, r); total_read += r; if (total_read != rv_get_reg(rv, rv_reg_a2) && ferror(handle)) { /* error */ @@ -314,7 +317,7 @@ static void syscall_fstat(riscv_t *rv UNUSED) static void syscall_open(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* _open(name, flags, mode); */ uint32_t name = rv_get_reg(rv, rv_reg_a0); @@ -323,8 +326,8 @@ static void syscall_open(riscv_t *rv) /* read name from runtime memory */ char name_str[256] = {'\0'}; - uint32_t read = - memory_read_str(s->mem, (uint8_t *) name_str, name, sizeof(name_str)); + uint32_t read = memory_read_str(attr->mem, (uint8_t *) name_str, name, + sizeof(name_str)); if (read > sizeof(name_str)) { rv_set_reg(rv, rv_reg_a0, -1); return; @@ -343,10 +346,10 @@ static void syscall_open(riscv_t *rv) return; } - const int fd = find_free_fd(s); /* find a free file descriptor */ + const int fd = find_free_fd(attr); /* find a free file descriptor */ /* insert into the file descriptor map */ - map_insert(s->fd_map, (void *) &fd, &handle); + map_insert(attr->fd_map, (void *) &fd, &handle); /* return the file descriptor */ rv_set_reg(rv, rv_reg_a0, fd); @@ -376,4 +379,12 @@ void syscall_handler(riscv_t *rv) fprintf(stderr, "unknown syscall %d\n", (int) syscall); break; } + + /* + * save return code + * + * the application decides the usage of the return code + */ + vm_attr_t *attr = PRIV(rv); + attr->error = rv_get_reg(rv, rv_reg_a0); } diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index 6bd7e216..36f39a90 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -17,6 +17,7 @@ #include #include "riscv.h" +#include "riscv_private.h" /* The DSITMBK sound effect in DOOM1.WAD uses a sample rate of 22050, but since * the game is played in single-player mode, it is acceptable to stick with @@ -149,10 +150,10 @@ static submission_queue_t submission_queue = { static submission_t submission_pop(riscv_t *rv) { - state_t *s = rv_userdata(rv); + vm_attr_t *attr = PRIV(rv); submission_t submission; memory_read( - s->mem, (void *) &submission, + attr->mem, (void *) &submission, submission_queue.base + submission_queue.start * sizeof(submission_t), sizeof(submission_t)); ++submission_queue.start; @@ -162,16 +163,17 @@ static submission_t submission_pop(riscv_t *rv) static void event_push(riscv_t *rv, event_t event) { - state_t *s = rv_userdata(rv); - memory_write(s->mem, event_queue.base + event_queue.end * sizeof(event_t), + vm_attr_t *attr = PRIV(rv); + memory_write(attr->mem, + event_queue.base + event_queue.end * sizeof(event_t), (void *) &event, sizeof(event_t)); ++event_queue.end; event_queue.end &= queues_capacity - 1; uint32_t count; - memory_read(s->mem, (void *) &count, event_count, sizeof(uint32_t)); + memory_read(attr->mem, (void *) &count, event_count, sizeof(uint32_t)); count += 1; - memory_write(s->mem, event_count, (void *) &count, sizeof(uint32_t)); + memory_write(attr->mem, event_count, (void *) &count, sizeof(uint32_t)); } static inline uint32_t round_pow2(uint32_t x) @@ -279,7 +281,7 @@ static bool check_sdl(riscv_t *rv, int width, int height) void syscall_draw_frame(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); /* draw_frame(base, width, height) */ const uint32_t screen = rv_get_reg(rv, rv_reg_a0); @@ -294,7 +296,7 @@ void syscall_draw_frame(riscv_t *rv) void *pixels_ptr; if (SDL_LockTexture(texture, NULL, &pixels_ptr, &pitch)) exit(-1); - memory_read(s->mem, pixels_ptr, screen, width * height * 4); + memory_read(attr->mem, pixels_ptr, screen, width * height * 4); SDL_UnlockTexture(texture); int actual_width, actual_height; @@ -677,13 +679,14 @@ static void *music_handler(void *arg) static void play_sfx(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); const uint32_t sfxinfo_addr = (uint32_t) rv_get_reg(rv, rv_reg_a1); int volume = rv_get_reg(rv, rv_reg_a2); sfxinfo_t sfxinfo; - memory_read(s->mem, (uint8_t *) &sfxinfo, sfxinfo_addr, sizeof(sfxinfo_t)); + memory_read(attr->mem, (uint8_t *) &sfxinfo, sfxinfo_addr, + sizeof(sfxinfo_t)); /* The data and size in the application must be positioned in the first two * fields of the structure. This ensures emulator compatibility with @@ -692,7 +695,7 @@ static void play_sfx(riscv_t *rv) uint32_t sfx_data_offset = *((uint32_t *) &sfxinfo); uint32_t sfx_data_size = *(uint32_t *) ((uint32_t *) &sfxinfo + 1); uint8_t sfx_data[SFX_SAMPLE_SIZE]; - memory_read(s->mem, sfx_data, sfx_data_offset, + memory_read(attr->mem, sfx_data, sfx_data_offset, sizeof(uint8_t) * sfx_data_size); sound_t sfx = { @@ -705,14 +708,14 @@ static void play_sfx(riscv_t *rv) static void play_music(riscv_t *rv) { - state_t *s = rv_userdata(rv); /* access userdata */ + vm_attr_t *attr = PRIV(rv); const uint32_t musicinfo_addr = (uint32_t) rv_get_reg(rv, rv_reg_a1); int volume = rv_get_reg(rv, rv_reg_a2); int looping = rv_get_reg(rv, rv_reg_a3); musicinfo_t musicinfo; - memory_read(s->mem, (uint8_t *) &musicinfo, musicinfo_addr, + memory_read(attr->mem, (uint8_t *) &musicinfo, musicinfo_addr, sizeof(musicinfo_t)); /* The data and size in the application must be positioned in the first two @@ -722,7 +725,7 @@ static void play_music(riscv_t *rv) uint32_t music_data_offset = *((uint32_t *) &musicinfo); uint32_t music_data_size = *(uint32_t *) ((uint32_t *) &musicinfo + 1); uint8_t music_data[MUSIC_MAX_SIZE]; - memory_read(s->mem, music_data, music_data_offset, music_data_size); + memory_read(attr->mem, music_data, music_data_offset, music_data_size); sound_t music = { .data = music_data, diff --git a/tools/gen-jit-template.py b/tools/gen-jit-template.py index 02c55f58..5199525b 100755 --- a/tools/gen-jit-template.py +++ b/tools/gen-jit-template.py @@ -219,7 +219,7 @@ def parse_argv(EXT_LIST, SKIP_LIST): elif items[0] == "jmp_off": asm = "emit_jump_target_offset(state, JUMP_LOC, state->offset);" elif items[0] == "mem": - asm = "memory_t *m = ((state_t *) rv->userdata)->mem;" + asm = "memory_t *m = PRIV(rv)->mem;" elif items[0] == "call": asm = "emit_call(state, (intptr_t) rv->io.on_{});".format( items[1])