diff --git a/Makefile b/Makefile index 317a7df11..3bdb1192c 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ OBJS := \ riscv.o \ elf.o \ cache.o \ + mpool.o \ $(OBJS_EXT) \ main.o diff --git a/src/emulate.c b/src/emulate.c index b267fff67..947f256ed 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -90,6 +90,24 @@ static void rv_exception_default_handler(riscv_t *rv) RV_EXCEPTION_LIST #undef _ +/* wrap load/store and insn misaligned handler + * @mask_or_pc: mask for load/store and pc for insn misaligned handler. + * @type: type of misaligned handler + * @compress: compressed instruction or not + * @IO: whether the misaligned handler is for load/store or insn. + */ +#define RV_EXC_MISALIGN_HANDLER(mask_or_pc, type, compress, IO) \ + IIF(IO) \ + (if (!rv->io.allow_misalign && unlikely(addr & (mask_or_pc))), \ + if (unlikely(insn_is_misaligned(rv->PC)))) \ + { \ + rv->compressed = compress; \ + IIF(IO) \ + (rv_except_##type##_misaligned(rv, addr), \ + rv_except_##type##_misaligned(rv, mask_or_pc)); \ + return false; \ + } + /* Get current time in microsecnds and update csr_time register */ static inline void update_time(riscv_t *rv) { @@ -310,11 +328,7 @@ RVOP(jal, { if (ir->rd) rv->X[ir->rd] = pc + ir->insn_len; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); return true; }) @@ -333,11 +347,7 @@ RVOP(jalr, { if (ir->rd) rv->X[ir->rd] = pc + ir->insn_len; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); return true; }) @@ -352,11 +362,7 @@ RVOP(beq, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -373,11 +379,7 @@ RVOP(bne, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -394,11 +396,7 @@ RVOP(blt, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -415,11 +413,7 @@ RVOP(bge, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -436,11 +430,7 @@ RVOP(bltu, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -457,11 +447,7 @@ RVOP(bgeu, { } rv->PC += ir->imm; /* check instruction misaligned */ - if (unlikely(insn_is_misaligned(rv->PC))) { - rv->compressed = false; - rv_except_insn_misaligned(rv, pc); - return false; - } + RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); if (ir->branch_taken) return ir->branch_taken->impl(rv, ir->branch_taken); return true; @@ -476,22 +462,14 @@ RVOP(lb, { /* LH: Load Halfword */ RVOP(lh, { const uint32_t addr = rv->X[ir->rs1] + ir->imm; - if (unlikely(addr & 1)) { - rv->compressed = false; - rv_except_load_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(1, load, false, 1); rv->X[ir->rd] = sign_extend_h(rv->io.mem_read_s(rv, addr)); }) /* LW: Load Word */ RVOP(lw, { const uint32_t addr = rv->X[ir->rs1] + ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = false; - rv_except_load_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, load, false, 1); rv->X[ir->rd] = rv->io.mem_read_w(rv, addr); }) @@ -501,11 +479,7 @@ RVOP(lbu, { rv->X[ir->rd] = rv->io.mem_read_b(rv, rv->X[ir->rs1] + ir->imm); }) /* LHU: Load Halfword Unsigned */ RVOP(lhu, { const uint32_t addr = rv->X[ir->rs1] + ir->imm; - if (unlikely(addr & 1)) { - rv->compressed = false; - rv_except_load_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(1, load, false, 1); rv->X[ir->rd] = rv->io.mem_read_s(rv, addr); }) @@ -515,22 +489,14 @@ RVOP(sb, { rv->io.mem_write_b(rv, rv->X[ir->rs1] + ir->imm, rv->X[ir->rs2]); }) /* SH: Store Halfword */ RVOP(sh, { const uint32_t addr = rv->X[ir->rs1] + ir->imm; - if (unlikely(addr & 1)) { - rv->compressed = false; - rv_except_store_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(1, store, false, 1); rv->io.mem_write_s(rv, addr, rv->X[ir->rs2]); }) /* SW: Store Word */ RVOP(sw, { const uint32_t addr = rv->X[ir->rs1] + ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = false; - rv_except_store_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, store, false, 1); rv->io.mem_write_w(rv, addr, rv->X[ir->rs2]); }) @@ -1088,11 +1054,7 @@ RVOP(caddi4spn, { rv->X[ir->rd] = rv->X[2] + (uint16_t) ir->imm; }) */ RVOP(clw, { const uint32_t addr = rv->X[ir->rs1] + (uint32_t) ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = true; - rv_except_load_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, load, true, 1); rv->X[ir->rd] = rv->io.mem_read_w(rv, addr); }) @@ -1103,11 +1065,7 @@ RVOP(clw, { */ RVOP(csw, { const uint32_t addr = rv->X[ir->rs1] + (uint32_t) ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = true; - rv_except_store_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, store, true, 1); rv->io.mem_write_w(rv, addr, rv->X[ir->rs2]); }) @@ -1126,11 +1084,7 @@ RVOP(caddi, { rv->X[ir->rd] += (int16_t) ir->imm; }) RVOP(cjal, { rv->X[1] = rv->PC + ir->insn_len; rv->PC += ir->imm; - if (unlikely(rv->PC & 0x1)) { - rv->compressed = true; - rv_except_insn_misaligned(rv, rv->PC); - return false; - } + RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); return true; }) @@ -1198,11 +1152,7 @@ RVOP(cand, { rv->X[ir->rd] = rv->X[ir->rs1] & rv->X[ir->rs2]; }) */ RVOP(cj, { rv->PC += ir->imm; - if (unlikely(rv->PC & 0x1)) { - rv->compressed = true; - rv_except_insn_misaligned(rv, rv->PC); - return false; - } + RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); return true; }) @@ -1249,11 +1199,7 @@ RVOP(cslli, { rv->X[ir->rd] <<= (uint8_t) ir->imm; }) /* C.LWSP */ RVOP(clwsp, { const uint32_t addr = rv->X[rv_reg_sp] + ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = true; - rv_except_load_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, load, true, 1); rv->X[ir->rd] = rv->io.mem_read_w(rv, addr); }) @@ -1279,11 +1225,7 @@ RVOP(cjalr, { const int32_t jump_to = rv->X[ir->rs1]; rv->X[rv_reg_ra] = rv->PC + ir->insn_len; rv->PC = jump_to; - if (unlikely(rv->PC & 0x1)) { - rv->compressed = true; - rv_except_insn_misaligned(rv, rv->PC); - return false; - } + RV_EXC_MISALIGN_HANDLER(rv->PC, insn, true, 0); return true; }) @@ -1299,11 +1241,7 @@ RVOP(cadd, { rv->X[ir->rd] = rv->X[ir->rs1] + rv->X[ir->rs2]; }) /* C.SWSP */ RVOP(cswsp, { const uint32_t addr = rv->X[2] + ir->imm; - if (unlikely(addr & 3)) { - rv->compressed = true; - rv_except_store_misaligned(rv, addr); - return false; - } + RV_EXC_MISALIGN_HANDLER(3, store, true, 1); rv->io.mem_write_w(rv, addr, rv->X[ir->rs2]); }) #endif diff --git a/src/io.c b/src/io.c index 732c2f5b2..e51cb2406 100644 --- a/src/io.c +++ b/src/io.c @@ -9,14 +9,17 @@ #include #include "io.h" +#include "mpool.h" static const uint32_t mask_lo = 0xffff; static const uint32_t mask_hi = ~(0xffff); +static struct mpool *mp; + memory_t *memory_new() { - memory_t *m = malloc(sizeof(memory_t)); - memset(m->chunks, 0, sizeof(m->chunks)); + memory_t *m = calloc(1, sizeof(memory_t)); + mp = mpool_create(sizeof(chunk_t) << 5, sizeof(chunk_t)); return m; } @@ -24,12 +27,7 @@ void memory_delete(memory_t *m) { if (!m) return; - - for (uint32_t i = 0; i < (sizeof(m->chunks) / sizeof(chunk_t *)); i++) { - chunk_t *c = m->chunks[i]; - if (c) - free(c); - } + mpool_destory(mp); free(m); } @@ -85,54 +83,53 @@ uint32_t memory_ifetch(memory_t *m, uint32_t addr) uint32_t memory_read_w(memory_t *m, uint32_t addr) { const uint32_t addr_lo = addr & mask_lo; - if (addr_lo <= 0xfffc) { /* test if this is within one chunk */ - chunk_t *c; - if ((c = m->chunks[addr >> 16])) - return *(const uint32_t *) (c->data + addr_lo); - return 0U; - } - uint32_t dst = 0; - memory_read(m, (uint8_t *) &dst, addr, 4); - return dst; + chunk_t *c = m->chunks[addr >> 16]; + return *(const uint32_t *) (c->data + addr_lo); } uint16_t memory_read_s(memory_t *m, uint32_t addr) { const uint32_t addr_lo = addr & mask_lo; - if (addr_lo <= 0xfffe) { /* test if this is within one chunk */ - chunk_t *c; - if ((c = m->chunks[addr >> 16])) - return *(const uint16_t *) (c->data + addr_lo); - return 0U; - } - uint16_t dst = 0; - memory_read(m, (uint8_t *) &dst, addr, 2); - return dst; + chunk_t *c = m->chunks[addr >> 16]; + return *(const uint16_t *) (c->data + addr_lo); } uint8_t memory_read_b(memory_t *m, uint32_t addr) { - chunk_t *c; - if ((c = m->chunks[addr >> 16])) - return *(c->data + (addr & 0xffff)); - return 0U; + chunk_t *c = m->chunks[addr >> 16]; + return c->data[addr & mask_lo]; } void memory_write(memory_t *m, uint32_t addr, const uint8_t *src, uint32_t size) { for (uint32_t i = 0; i < size; ++i) { - uint32_t p = addr + i; - uint32_t x = p >> 16; - chunk_t *c = m->chunks[x]; + const uint32_t addr_lo = (addr + i) & mask_lo, + addr_hi = (addr + i) >> 16; + chunk_t *c = m->chunks[addr_hi]; if (!c) { - c = malloc(sizeof(chunk_t)); - memset(c->data, 0, sizeof(c->data)); - m->chunks[x] = c; + c = mpool_calloc(mp); + m->chunks[addr_hi] = c; } - c->data[p & 0xffff] = src[i]; + c->data[addr_lo] = src[i]; } } +#define memory_write(size, type) \ + void memory_write_##size(memory_t *m, uint32_t addr, const uint8_t *src) \ + { \ + const uint32_t addr_lo = addr & mask_lo, addr_hi = addr >> 16; \ + chunk_t *c = m->chunks[addr_hi]; \ + if (unlikely(!c)) { \ + c = mpool_calloc(mp); \ + m->chunks[addr_hi] = c; \ + } \ + *(type *) (c->data + addr_lo) = *(const type *) src; \ + } + +memory_write(w, uint32_t); +memory_write(s, uint16_t); +memory_write(b, uint8_t); + void memory_fill(memory_t *m, uint32_t addr, uint32_t size, uint8_t val) { for (uint32_t i = 0; i < size; ++i) { @@ -140,8 +137,7 @@ void memory_fill(memory_t *m, uint32_t addr, uint32_t size, uint8_t val) uint32_t x = p >> 16; chunk_t *c = m->chunks[x]; if (!c) { - c = malloc(sizeof(chunk_t)); - memset(c->data, 0, sizeof(c->data)); + c = mpool_calloc(mp); m->chunks[x] = c; } c->data[p & 0xffff] = val; diff --git a/src/io.h b/src/io.h index 1793ce0dc..79f2dc495 100644 --- a/src/io.h +++ b/src/io.h @@ -43,4 +43,11 @@ void memory_write(memory_t *m, uint32_t addr, const uint8_t *src, uint32_t size); + +void memory_write_w(memory_t *m, uint32_t addr, const uint8_t *src); + +void memory_write_s(memory_t *m, uint32_t addr, const uint8_t *src); + +void memory_write_b(memory_t *m, uint32_t addr, const uint8_t *src); + void memory_fill(memory_t *m, uint32_t addr, uint32_t size, uint8_t val); diff --git a/src/main.c b/src/main.c index 4c6d7e488..6a615498c 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,9 @@ static char *signature_out_file; /* target executable */ static const char *opt_prog_name = "a.out"; +/* enable misaligned memory access */ +static bool opt_misaligned = false; + #define MEMIO(op) on_mem_##op #define IO_HANDLER_IMPL(type, op, RW) \ static IIF(RW)( \ @@ -34,7 +37,7 @@ static const char *opt_prog_name = "a.out"; { \ state_t *s = rv_userdata(rv); \ IIF(RW) \ - (memory_write(s->mem, addr, (uint8_t *) &data, sizeof(data)), \ + (memory_##op(s->mem, addr, (uint8_t *) &data), \ return memory_##op(s->mem, addr)); \ } @@ -123,6 +126,12 @@ static bool parse_args(int argc, char **args) signature_out_file = args[++i]; continue; } + + if (!strcmp(arg, "--misalign")) { + opt_misaligned = true; + continue; + } + /* otherwise, error */ fprintf(stderr, "Unknown argument '%s'\n", arg); return false; @@ -192,6 +201,7 @@ int main(int argc, char **args) /* system */ .on_ecall = ecall_handler, .on_ebreak = ebreak_handler, + .allow_misalign = opt_misaligned, }; state_t *state = state_new(); diff --git a/src/mpool.c b/src/mpool.c new file mode 100644 index 000000000..9c4730fe9 --- /dev/null +++ b/src/mpool.c @@ -0,0 +1,104 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#include "mpool.h" + +typedef struct memchunk { + struct memchunk *next; +} memchunk_t; + +typedef struct mpool { + size_t chunk_count; + size_t page_count; + size_t chunk_size; + char *mapped; + struct memchunk *free_chunk_head; + struct mpool *next; +} mpool_t; + +static void *get_mmap(size_t sz) +{ +#if defined(USE_MMAP) + void *p = + mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (p == MAP_FAILED) + return NULL; +#else + void *p = malloc(sz); + if (!p) + return NULL; +#endif + return p; +} + +mpool_t *mpool_create(size_t pool_size, size_t chunk_size) +{ + mpool_t *new_mp = malloc(sizeof(mpool_t)); + if (!new_mp) + return NULL; + + new_mp->next = NULL; + size_t pgsz = getpagesize(); + size_t page_count = (pool_size + pgsz - 1) / pgsz; + char *p = get_mmap(page_count * pgsz); + if (!p) { + free(new_mp); + return NULL; + } + + new_mp->mapped = p; + new_mp->page_count = page_count; + new_mp->chunk_count = pool_size / (sizeof(memchunk_t) + chunk_size); + new_mp->chunk_size = chunk_size; + new_mp->free_chunk_head = (memchunk_t *) p; + memchunk_t *cur = new_mp->free_chunk_head; + for (size_t i = 0; i < new_mp->chunk_count - 1; i++) { + cur->next = + (memchunk_t *) ((char *) cur + (sizeof(memchunk_t) + chunk_size)); + cur = cur->next; + } + cur->next = NULL; + return new_mp; +} + +void *mpool_alloc(mpool_t *mp) +{ + if (!mp->chunk_count) + return NULL; + char *ptr = (char *) mp->free_chunk_head + sizeof(memchunk_t); + mp->free_chunk_head = mp->free_chunk_head->next; + mp->chunk_count--; + return ptr; +} + +void *mpool_calloc(mpool_t *mp) +{ + if (mp->chunk_count == 0) + return NULL; + char *ptr = (char *) mp->free_chunk_head + sizeof(memchunk_t); + mp->free_chunk_head = mp->free_chunk_head->next; + mp->chunk_count--; + memset(ptr, 0, mp->chunk_size); + return ptr; +} + +void mpool_free(mpool_t *mp, void *target) +{ + memchunk_t *ptr = (memchunk_t *) ((char *) target - sizeof(memchunk_t)); + ptr->next = mp->free_chunk_head; + mp->free_chunk_head = ptr; + mp->chunk_count++; +} + +void mpool_destory(mpool_t *mp) +{ +#if defined(USE_MMAP) + size_t pgsz = getpagesize(); + munmap(mp->mapped, mp->page_count * pgsz); +#else + free(mp->mapped); +#endif + free(mp); +} \ No newline at end of file diff --git a/src/mpool.h b/src/mpool.h new file mode 100644 index 000000000..507cf20b0 --- /dev/null +++ b/src/mpool.h @@ -0,0 +1,24 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ +#pragma once + +#include +#include +#include +#include + +#define USE_MMAP 1 + +struct mpool; + +struct mpool *mpool_create(size_t pool_size, size_t chunk_size); + +void *mpool_alloc(struct mpool *mp); + +void *mpool_calloc(struct mpool *mp); + +void mpool_free(struct mpool *mp, void *target); + +void mpool_destory(struct mpool *mp); \ No newline at end of file diff --git a/src/riscv.h b/src/riscv.h index cb96847d8..271bdc242 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -107,6 +107,9 @@ typedef struct { /* system */ riscv_on_ecall on_ecall; riscv_on_ebreak on_ebreak; + + /* enable misaligned memory access */ + bool allow_misalign; } riscv_io_t; /* create a RISC-V emulator */ diff --git a/src/syscall.c b/src/syscall.c index 166f2899f..bdcd7a139 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -142,8 +142,8 @@ static void syscall_gettimeofday(riscv_t *rv) if (tv) { struct timeval tv_s; rv_gettimeofday(&tv_s); - memory_write(s->mem, tv + 0, (const uint8_t *) &tv_s.tv_sec, 4); - memory_write(s->mem, tv + 8, (const uint8_t *) &tv_s.tv_usec, 4); + memory_write_w(s->mem, tv + 0, (const uint8_t *) &tv_s.tv_sec); + memory_write_w(s->mem, tv + 8, (const uint8_t *) &tv_s.tv_usec); } if (tz) { @@ -176,8 +176,8 @@ static void syscall_clock_gettime(riscv_t *rv) if (tp) { struct timespec tp_s; rv_clock_gettime(&tp_s); - memory_write(s->mem, tp + 0, (const uint8_t *) &tp_s.tv_sec, 4); - memory_write(s->mem, tp + 8, (const uint8_t *) &tp_s.tv_nsec, 4); + memory_write_w(s->mem, tp + 0, (const uint8_t *) &tp_s.tv_sec); + memory_write_w(s->mem, tp + 8, (const uint8_t *) &tp_s.tv_nsec); } /* success */ diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index 1fe1648d5..95fdc79a3 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -110,7 +110,7 @@ static void event_push(riscv_t *rv, event_t event) uint32_t count; memory_read(s->mem, (void *) &count, event_count, sizeof(uint32_t)); count += 1; - memory_write(s->mem, event_count, (void *) &count, sizeof(uint32_t)); + memory_write_w(s->mem, event_count, (void *) &count); } static inline uint32_t round_pow2(uint32_t x)