Skip to content

Commit 820cd9b

Browse files
committed
Refine the API in the public header
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 needs to manage about memory (state_t) and elf stuff before this commit. The user can just construct a core, run it, and shut it down after this commit, 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. As you can see in "main", there are various mode to run the emulator such as "run_and_trace", "gdbstub", and "profiling". Thus, a field call "run_flag" is introduced in vm_attr_t. For standard stream remapping, rv_remap_stdstream function is introduced. The emulator can remap default standard stream to required streams after creating the emulator by calling the rv_remap_stdstream function. rv_userdata has been dropped since PRIV macro is sufficient for internal implemntation. Also, application will not need to access it directly. elf is reopened in dump_test_signature because elf is allocated during rv_create. It is acceptable to reopen elf since it is only for testing. 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 commit because it could maybe reused with semu in some way, still need to be investigated. Logging feature and system emulator integration are not implemented yet. Also, a validator for validating the user-defined vm_attr_t might need to be introduced. related: #310
1 parent 176e121 commit 820cd9b

File tree

14 files changed

+391
-235
lines changed

14 files changed

+391
-235
lines changed

Makefile

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ CFLAGS = -std=gnu99 -O2 -Wall -Wextra
1111
CFLAGS += -Wno-unused-label
1212
CFLAGS += -include src/common.h
1313

14-
# Set the default stack pointer
15-
CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFE000
16-
# Set the default args starting address
17-
CFLAGS += -D DEFAULT_ARGS_ADDR=0xFFFFF000
18-
1914
# Enable link-time optimization (LTO)
2015
ENABLE_LTO ?= 1
2116
ifeq ($(call has, LTO), 1)
@@ -121,7 +116,7 @@ endif
121116
ENABLE_JIT ?= 0
122117
$(call set-feature, JIT)
123118
ifeq ($(call has, JIT), 1)
124-
OBJS_EXT += jit.o
119+
OBJS_EXT += jit.o
125120
ifneq ($(processor),$(filter $(processor),x86_64 aarch64 arm64))
126121
$(error JIT mode only supports for x64 and arm64 target currently.)
127122
endif

src/elf.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,8 @@ bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end)
259259
* Finding data for section headers:
260260
* File start + section_header.offset -> section Data
261261
*/
262-
bool elf_load(elf_t *e, riscv_t *rv, memory_t *mem)
262+
bool elf_load(elf_t *e, memory_t *mem)
263263
{
264-
/* set the entry point */
265-
if (!rv_set_pc(rv, e->hdr->e_entry))
266-
return false;
267-
268264
/* loop over all of the program headers */
269265
for (int p = 0; p < e->hdr->e_phnum; ++p) {
270266
/* find next program header */

src/elf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ const char *elf_find_symbol(elf_t *e, uint32_t addr);
145145
bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end);
146146

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

150150
/* get the ELF header */
151151
struct Elf32_Ehdr *get_elf_header(elf_t *e);

src/emulate.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ RV_EXCEPTION_LIST
125125
*/
126126
#define RV_EXC_MISALIGN_HANDLER(mask_or_pc, type, compress, IO) \
127127
IIF(IO) \
128-
(if (!rv->io.allow_misalign && unlikely(addr & (mask_or_pc))), \
128+
(if (!PRIV(rv)->allow_misalign && unlikely(addr & (mask_or_pc))), \
129129
if (unlikely(insn_is_misaligned(PC)))) \
130130
{ \
131131
rv->compressed = compress; \
@@ -1182,15 +1182,15 @@ void ecall_handler(riscv_t *rv)
11821182

11831183
void memset_handler(riscv_t *rv)
11841184
{
1185-
memory_t *m = ((state_t *) rv->userdata)->mem;
1185+
memory_t *m = PRIV(rv)->mem;
11861186
memset((char *) m->mem_base + rv->X[rv_reg_a0], rv->X[rv_reg_a1],
11871187
rv->X[rv_reg_a2]);
11881188
rv->PC = rv->X[rv_reg_ra] & ~1U;
11891189
}
11901190

11911191
void memcpy_handler(riscv_t *rv)
11921192
{
1193-
memory_t *m = ((state_t *) rv->userdata)->mem;
1193+
memory_t *m = PRIV(rv)->mem;
11941194
memcpy((char *) m->mem_base + rv->X[rv_reg_a0],
11951195
(char *) m->mem_base + rv->X[rv_reg_a1], rv->X[rv_reg_a2]);
11961196
rv->PC = rv->X[rv_reg_ra] & ~1U;

src/io.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,36 @@
1717

1818
static uint8_t *data_memory_base;
1919

20-
/*
21-
* set memory size to 2^32 - 1 bytes
22-
*
23-
* The memory size is set to 2^32 - 1 bytes in order to make this emulator
24-
* portable for both 32-bit and 64-bit platforms. As a result, it can access
25-
* any segment of the memory on either platform. Furthermore, it is safe
26-
* because most of the test cases' data memory usage will not exceed this
27-
* memory size.
28-
*/
29-
#define MEM_SIZE 0xFFFFFFFFULL
30-
31-
memory_t *memory_new(void)
20+
memory_t *memory_new(uint32_t size)
3221
{
22+
if (!size)
23+
return NULL;
24+
3325
memory_t *mem = malloc(sizeof(memory_t));
3426
assert(mem);
3527
#if HAVE_MMAP
36-
data_memory_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
28+
data_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE,
3729
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
3830
if (data_memory_base == MAP_FAILED) {
3931
free(mem);
4032
return NULL;
4133
}
4234
#else
43-
data_memory_base = malloc(MEM_SIZE);
35+
data_memory_base = malloc(size);
4436
if (!data_memory_base) {
4537
free(mem);
4638
return NULL;
4739
}
4840
#endif
4941
mem->mem_base = data_memory_base;
50-
mem->mem_size = MEM_SIZE;
42+
mem->mem_size = size;
5143
return mem;
5244
}
5345

5446
void memory_delete(memory_t *mem)
5547
{
5648
#if HAVE_MMAP
57-
munmap(mem->mem_base, MEM_SIZE);
49+
munmap(mem->mem_base, mem->mem_size);
5850
#else
5951
free(mem->mem_base);
6052
#endif

src/io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ typedef struct {
1717
uint64_t mem_size;
1818
} memory_t;
1919

20-
memory_t *memory_new(void);
20+
memory_t *memory_new(uint32_t size);
2121
void memory_delete(memory_t *m);
2222

2323
/* read a C-style string from memory */

src/jit.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "io.h"
4040
#include "jit.h"
4141
#include "riscv.h"
42+
#include "riscv_private.h"
4243
#include "utils.h"
4344

4445
#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)
12841285

12851286
static void do_fuse3(struct jit_state *state, riscv_t *rv, rv_insn_t *ir)
12861287
{
1287-
memory_t *m = ((state_t *) rv->userdata)->mem;
1288+
memory_t *m = PRIV(rv)->mem;
12881289
opcode_fuse_t *fuse = ir->fuse;
12891290
for (int i = 0; i < ir->imm2; i++) {
12901291
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)
13001301

13011302
static void do_fuse4(struct jit_state *state, riscv_t *rv, rv_insn_t *ir)
13021303
{
1303-
memory_t *m = ((state_t *) rv->userdata)->mem;
1304+
memory_t *m = PRIV(rv)->mem;
13041305
opcode_fuse_t *fuse = ir->fuse;
13051306
for (int i = 0; i < ir->imm2; i++) {
13061307
emit_load(state, S32, parameter_reg[0], temp_reg[0],

src/main.c

Lines changed: 36 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -74,35 +74,10 @@ IO_HANDLER_IMPL(byte, write_b, W)
7474
#undef R
7575
#undef W
7676

77-
/* run: printing out an instruction trace */
78-
static void run_and_trace(riscv_t *rv, elf_t *elf)
79-
{
80-
const uint32_t cycles_per_step = 1;
81-
82-
for (; !rv_has_halted(rv);) { /* run until the flag is done */
83-
/* trace execution */
84-
uint32_t pc = rv_get_pc(rv);
85-
const char *sym = elf_find_symbol(elf, pc);
86-
printf("%08x %s\n", pc, (sym ? sym : ""));
87-
88-
/* step instructions */
89-
rv_step(rv, cycles_per_step);
90-
}
91-
}
92-
93-
static void run(riscv_t *rv)
94-
{
95-
const uint32_t cycles_per_step = 100;
96-
for (; !rv_has_halted(rv);) { /* run until the flag is done */
97-
/* step instructions */
98-
rv_step(rv, cycles_per_step);
99-
}
100-
}
101-
10277
static void print_usage(const char *filename)
10378
{
10479
fprintf(stderr,
105-
"RV32I[MA] Emulator which loads an ELF file to execute.\n"
80+
"RV32I[MACF] Emulator which loads an ELF file to execute.\n"
10681
"Usage: %s [options] [filename] [arguments]\n"
10782
"Options:\n"
10883
" -t : print executable trace\n"
@@ -188,8 +163,11 @@ static bool parse_args(int argc, char **args)
188163
return true;
189164
}
190165

191-
static void dump_test_signature(elf_t *elf)
166+
static void dump_test_signature(const char *prog_name)
192167
{
168+
elf_t *elf = elf_new();
169+
assert(elf && elf_open(elf, prog_name));
170+
193171
uint32_t start = 0, end = 0;
194172
const struct Elf32_Sym *sym;
195173
FILE *f = fopen(signature_out_file, "w");
@@ -212,21 +190,42 @@ static void dump_test_signature(elf_t *elf)
212190
fprintf(f, "%08x\n", memory_read_w(addr));
213191

214192
fclose(f);
193+
elf_delete(elf);
215194
}
216195

196+
#define MEM_SIZE 0xFFFFFFFFULL /* 2^32 - 1 */
197+
#define STACK_SIZE 0x1000 /* 4096 */
198+
#define ARGS_OFFSET_SIZE 0x1000 /* 4096 */
199+
217200
int main(int argc, char **args)
218201
{
219202
if (argc == 1 || !parse_args(argc, args)) {
220203
print_usage(args[0]);
221204
return 1;
222205
}
223206

224-
/* open the ELF file from the file system */
225-
elf_t *elf = elf_new();
226-
if (!elf_open(elf, opt_prog_name)) {
227-
fprintf(stderr, "Unable to open ELF file '%s'\n", opt_prog_name);
228-
return 1;
229-
}
207+
int run_flag = 0;
208+
run_flag |= opt_trace;
209+
#if RV32_HAS(GDBSTUB)
210+
run_flag |= opt_gdbstub << 1;
211+
#endif
212+
run_flag |= opt_prof_data << 2;
213+
214+
vm_attr_t attr = {
215+
.mem_size = MEM_SIZE,
216+
.stack_size = STACK_SIZE,
217+
.args_offset_size = ARGS_OFFSET_SIZE,
218+
.argc = prog_argc,
219+
.argv = prog_args,
220+
.log_level = 0,
221+
.run_flag = run_flag,
222+
.profile_output_file = prof_out_file,
223+
.data.user = malloc(sizeof(vm_user_t)),
224+
.cycle_per_step = 100,
225+
.allow_misalign = opt_misaligned,
226+
};
227+
assert(attr.data.user);
228+
attr.data.user->elf_program = opt_prog_name;
230229

231230
/* install the I/O handlers for the RISC-V runtime */
232231
const riscv_io_t io = {
@@ -246,57 +245,28 @@ int main(int argc, char **args)
246245
.on_ebreak = ebreak_handler,
247246
.on_memcpy = memcpy_handler,
248247
.on_memset = memset_handler,
249-
.allow_misalign = opt_misaligned,
250248
};
251249

252-
state_t *state = state_new();
253-
254-
/* find the start of the heap */
255-
const struct Elf32_Sym *end;
256-
if ((end = elf_get_symbol(elf, "_end")))
257-
state->break_addr = end->st_value;
258-
259250
/* create the RISC-V runtime */
260-
riscv_t *rv =
261-
rv_create(&io, state, prog_argc, prog_args, !opt_quiet_outputs);
251+
riscv_t *rv = rv_create(&io, &attr);
262252
if (!rv) {
263253
fprintf(stderr, "Unable to create riscv emulator\n");
264254
return 1;
265255
}
266256

267-
/* load the ELF file into the memory abstraction */
268-
if (!elf_load(elf, rv, state->mem)) {
269-
fprintf(stderr, "Unable to load ELF file '%s'\n", args[1]);
270-
return 1;
271-
}
272-
273-
/* run based on the specified mode */
274-
if (opt_trace) {
275-
run_and_trace(rv, elf);
276-
}
277-
#if RV32_HAS(GDBSTUB)
278-
else if (opt_gdbstub) {
279-
rv_debug(rv);
280-
}
281-
#endif
282-
else {
283-
run(rv);
284-
}
257+
rv_run(rv);
285258

286259
/* dump registers as JSON */
287260
if (opt_dump_regs)
288261
dump_registers(rv, registers_out_file);
289262

290263
/* dump test result in test mode */
291264
if (opt_arch_test)
292-
dump_test_signature(elf);
265+
dump_test_signature(opt_prog_name);
293266

294-
if (opt_prof_data)
295-
rv_profile(rv, prof_out_file);
296267
/* finalize the RISC-V runtime */
297-
elf_delete(elf);
298268
rv_delete(rv);
299-
state_delete(state);
300269

270+
printf("inferior exit code %d\n", attr.exit_code);
301271
return 0;
302272
}

0 commit comments

Comments
 (0)