diff --git a/Makefile b/Makefile index cda3c5027..c3641968a 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 7d4414020..4f3f4e48a 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 e43caf9a0..6ebb80723 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 ada47350a..dd3886ce7 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 ce66ce465..6da5d284c 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 cc93008f0..9a6741796 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 a24648dae..8c72658d6 100644 --- a/src/jit.c +++ b/src/jit.c @@ -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], @@ -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], diff --git a/src/main.c b/src/main.c index 71d900b71..28a468db7 100644 --- a/src/main.c +++ b/src/main.c @@ -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 */ @@ -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) @@ -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"); @@ -212,8 +210,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 +224,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 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 = { @@ -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) { @@ -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; } diff --git a/src/riscv.c b/src/riscv.c index 1399ff6bf..f835e2550 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -8,6 +8,7 @@ #include #include +#include "elf.h" #include "mpool.h" #include "riscv.h" #include "riscv_private.h" @@ -105,11 +106,65 @@ 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 stdio + * + * @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) +{ + if (!rv) + return; + + vm_attr_t *attr = PRIV(rv); + if (!attr) + return; + if (!attr->fd_map) + return; + + for (uint32_t i = 0; i < fsp_size; i++) { + int fd = fsp[i].fd; + FILE *file = fsp[i].file; + if (fd != 0 && fd != 1 && fd != 2) + continue; + if (!file) + continue; + + /* + * check if stdio_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); + switch (fd) { + case 0: + attr->stdin_fd = new_fd; + break; + case 1: + attr->stdout_fd = new_fd; + break; + case 2: + attr->stderr_fd = new_fd; + break; + default: + break; + } + } +} + +riscv_t *rv_create(const riscv_io_t *io, riscv_user_t userdata) { assert(io); @@ -122,7 +177,45 @@ riscv_t *rv_create(const riscv_io_t *io, /* copy over the userdata */ rv->userdata = userdata; - rv->output_exit_code = output_exit_code; + vm_attr_t *attr = PRIV(rv); + attr->mem = memory_new(attr->mem_size); + assert(attr->mem); + + /* reset */ + rv_reset(rv, 0U); + + if (attr->emu_data.vm_user) { + elf_t *elf = elf_new(); + assert(elf && elf_open(elf, (attr->emu_data.vm_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)); + + /* 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 stdio + * + * 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[]){ + {0, stdin}, + {1, stdout}, + {2, stderr}, + }, + 3); /* create block and IRs memory pool */ rv->block_mp = mpool_create(sizeof(block_t) << BLOCK_MAP_CAPACITY_BITS, @@ -141,12 +234,20 @@ 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; } +void run(riscv_t *rv) +{ + vm_attr_t *attr = PRIV(rv); + + for (; !rv_has_halted(rv);) { /* run until the flag is done */ + /* step instructions */ + rv_step(rv, attr->cycle_per_step); + } +} + void rv_halt(riscv_t *rv) { rv->halt = true; @@ -157,15 +258,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 +274,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 +323,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 +343,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 +360,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 +369,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 +403,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 b109a12ab..9b91eabc8 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -7,6 +7,7 @@ #include #include +#include #include "io.h" #include "map.h" @@ -97,6 +98,8 @@ enum { #define BLOCK_MAP_CAPACITY_BITS 10 +#define PRIV(x) ((vm_attr_t *) x->userdata) + /* forward declaration for internal structure */ typedef struct riscv_internal riscv_t; typedef void *riscv_user_t; @@ -143,22 +146,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 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 user_data); /* 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,7 +168,19 @@ 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 */ +/* + * @rv: RICV-V + * + * get RISC-V user data bound to an emulator + * + * A structure named "vm_attr_t" must first be set up by the user + * before a riscv core can be created. This structure then be passed + * to the "rv_create" function. + * + * The "vm_attr_t" that the user supplied is therefore regarded as user data. + * Thus, "vm_attr_t" will be returned by calling "rv_userdata". + * + */ riscv_user_t rv_userdata(riscv_t *rv); /* set the program counter of a RISC-V emulator */ @@ -180,6 +192,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 stdio 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,25 +227,82 @@ 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); +typedef struct { + char *elf_program; +} vm_user_t; + +typedef struct { + char *kernel_img; + char *dtb; + char *rootfs_img; +} vm_sys_t; + +typedef union { + vm_user_t *vm_user; + vm_sys_t *vm_sys; +} vm_emu_data_t; -/* state structure passed to the runtime */ 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 logging_level; -/* create a state */ -state_t *state_new(void); + /* userspace or system emulation data */ + vm_emu_data_t emu_data; -/* delete a state */ -void state_delete(state_t *s); + /* number of cycle(instruction) in a rv_step call*/ + int cycle_per_step; + + /* allow misaligned memory access */ + bool allow_misalign; + + /* + * default stdin, stdout, stderr, set by rv_create + * + * use rv_remap_stdstream to overwrite them + */ + int stdin_fd; + int stdout_fd; + int stderr_fd; + + /* vm file descriptor map: int -> (FILE *) */ + map_t fd_map; + + /* the data segment break address */ + riscv_word_t break_addr; +} vm_attr_t; void rv_profile(riscv_t *rv, char *out_file_path); diff --git a/src/riscv_private.h b/src/riscv_private.h index 0ec29bd4a..c1d2f9883 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -129,8 +129,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 8c2e867fa..fb409e183 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -53,12 +53,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 +80,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 = rv_userdata(rv); /* access userdata */ /* _write(fde, buffer, count) */ riscv_word_t fd = rv_get_reg(rv, rv_reg_a0); @@ -89,12 +89,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 +105,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); @@ -130,11 +130,13 @@ static void syscall_exit(riscv_t *rv) { 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); - } + /* + * simply halt cpu and save exit code + * + * the application decides the usage of exit code + */ + vm_attr_t *attr = rv_userdata(rv); /* access userdata */ + attr->exit_code = rv_get_reg(rv, rv_reg_a0); } /* brk(increment) @@ -144,15 +146,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 = rv_userdata(rv); /* access userdata */ /* 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 +209,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 = rv_userdata(rv); /* access userdata */ /* _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 +240,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 = rv_userdata(rv); /* access userdata */ /* _lseek(fd, offset, whence); */ uint32_t fd = rv_get_reg(rv, rv_reg_a0); @@ -247,8 +249,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 +269,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 = rv_userdata(rv); /* access userdata */ /* _read(fd, buf, count); */ uint32_t fd = rv_get_reg(rv, rv_reg_a0); @@ -276,8 +278,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 +291,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 +316,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 = rv_userdata(rv); /* access userdata */ /* _open(name, flags, mode); */ uint32_t name = rv_get_reg(rv, rv_reg_a0); @@ -323,8 +325,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 +345,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 +378,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 = rv_userdata(rv); /* access userdata */ + attr->error = rv_get_reg(rv, rv_reg_a0); } diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index 6bd7e2166..a78ab1959 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -149,10 +149,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 = rv_userdata(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 +162,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 = rv_userdata(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 +280,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 = rv_userdata(rv); /* access userdata */ /* draw_frame(base, width, height) */ const uint32_t screen = rv_get_reg(rv, rv_reg_a0); @@ -294,7 +295,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 +678,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 = rv_userdata(rv); /* access userdata */ 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 +694,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 +707,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 = rv_userdata(rv); /* access userdata */ 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 +724,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 02c55f586..5199525b7 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])