summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--msim/debug.c79
-rw-r--r--msim/fault.c56
-rw-r--r--msim/fault.h10
-rw-r--r--msim/ins.c373
-rw-r--r--msim/load.c226
-rw-r--r--msim/main.c74
-rw-r--r--msim/sim.c93
-rw-r--r--msim/sim.h87
8 files changed, 995 insertions, 3 deletions
diff --git a/msim/debug.c b/msim/debug.c
new file mode 100644
index 0000000..1613c76
--- /dev/null
+++ b/msim/debug.c
@@ -0,0 +1,79 @@
+#include <merror.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <melf.h>
+
+#include "sim.h"
+
+static uint32_t read_num() {
+ int i = 0;
+ while (1) {
+ int c = getc(stdin);
+ if (c < '0' || c > '9') {
+ ungetc(c, stdin);
+ break;
+ }
+ i *= 10;
+ i += c - '0';
+ }
+
+ if (i)
+ return i;
+ return 1;
+}
+
+_Noreturn void sim_debug(struct simulator *sim) {
+
+ int c;
+ sim->pc = sim->entry;
+ sim->current_pc = sim->pc;
+
+prompt:
+ printf(">> ");
+ fflush(stdout);
+next:
+ switch ((c = getchar())) {
+
+ /* skip whitespace */
+ case ' ':
+ case '\t':
+ goto next;
+
+ /* ignore nl */
+ case '\n':
+ goto prompt;
+
+ /* exit on eof */
+ case EOF:
+ exit(0);
+ break;
+
+ /* dump registers */
+ case 'r':
+ sim_dump_reg(sim);
+ break;
+
+ /* step ins */
+ case 's': {
+ for (uint32_t i = read_num(); i > 0; i--)
+ sim_step(sim);
+ break;
+ }
+
+ /* print curr ins */
+ case 'i':
+ printf("0x%08x\n", B32(* (uint32_t *) (uintptr_t) sim->pc));
+ break;
+
+ case 'a':
+ printf("0x%08x\n", sim->pc);
+ break;
+
+ default:
+ ERROR("unknown command '%c'", c);
+ break;
+ }
+
+ goto next;
+}
diff --git a/msim/fault.c b/msim/fault.c
new file mode 100644
index 0000000..88cd340
--- /dev/null
+++ b/msim/fault.c
@@ -0,0 +1,56 @@
+#include "fault.h"
+#include "sim.h"
+#include <signal.h>
+#include <time.h>
+
+static struct simulator *sim;
+
+static void sigsegv_handler(int sig, siginfo_t *info, void *ucontext)
+{
+ (void) sig;
+ (void) ucontext;
+
+ const char *inner = "";
+ switch (info->si_code) {
+ case SEGV_MAPERR:
+ inner = ": address not mapped to object";
+ break;
+ case SEGV_ACCERR:
+ inner = ": invalid premissions for mapped object";
+ break;
+ case SEGV_BNDERR:
+ inner = ": failed address bound checks";
+ break;
+ case SEGV_PKUERR:
+ inner = ": access was denied by memory protection";
+ break;
+ }
+
+ sim_dump(sim, "page fault at %p%s", info->si_addr, inner);
+}
+
+static void sigfpe_handler(int sig, siginfo_t *info, void *ucontext)
+{
+ (void) sig;
+ (void) info;
+ (void) ucontext;
+
+ sim_dump(sim, "floating point exception");
+
+}
+
+void init_handlers(struct simulator *s)
+{
+ struct sigaction act = {0};
+ sim = s;
+
+ /* sigsegv */
+ act.sa_sigaction = sigsegv_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &act, NULL);
+
+ /* sigfpe */
+ act.sa_sigaction = sigfpe_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGFPE, &act, NULL);
+}
diff --git a/msim/fault.h b/msim/fault.h
new file mode 100644
index 0000000..26371b3
--- /dev/null
+++ b/msim/fault.h
@@ -0,0 +1,10 @@
+/* Copyright (c) 2024 Freya Murphy */
+
+#ifndef __FAULT_H__
+#define __FAULT_H__
+
+#include "sim.h"
+
+void init_handlers(struct simulator *s);
+
+#endif /* __FAULT_H__ */
diff --git a/msim/ins.c b/msim/ins.c
new file mode 100644
index 0000000..6a98e92
--- /dev/null
+++ b/msim/ins.c
@@ -0,0 +1,373 @@
+#include <mips.h>
+#include <melf.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "sim.h"
+
+#define I16(n) ((int32_t)(uint16_t)(n))
+
+static void sim_ins_special_sop30(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.shamt) {
+ case MIPS_SOP30_MUL:
+ sim->reg[ins.rd] = ((int64_t)sim->reg[ins.rs] /
+ (int64_t)sim->reg[ins.rt]) >> 0;
+ break;
+
+ case MIPS_SOP30_MUH:
+ sim->reg[ins.rd] = ((int64_t)sim->reg[ins.rs] /
+ (int64_t)sim->reg[ins.rt]) >> 32;
+ break;
+
+ default:
+ sim_dump(sim, "unknown sop30 funct (0b%06b)", ins.shamt);
+ }
+}
+
+static void sim_ins_special_sop31(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.shamt) {
+ case MIPS_SOP31_MULU:
+ sim->reg[ins.rd] = ((uint64_t)sim->reg[ins.rs] /
+ (uint64_t)sim->reg[ins.rt]) >> 0;
+ break;
+
+ case MIPS_SOP31_MUHU:
+ sim->reg[ins.rd] = ((uint64_t)sim->reg[ins.rs] /
+ (uint64_t)sim->reg[ins.rt]) >> 32;
+ break;
+
+ default:
+ sim_dump(sim, "unknown sop31 funct (0b%06b)", ins.shamt);
+ }
+}
+
+static void sim_ins_special_sop32(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.shamt) {
+ case MIPS_SOP32_DIV:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] /
+ (int32_t)sim->reg[ins.rt];
+ break;
+
+ case MIPS_SOP32_MOD:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] %
+ (int32_t)sim->reg[ins.rt];
+ break;
+
+ default:
+ sim_dump(sim, "unknown sop32 funct (0b%06b)", ins.shamt);
+ }
+}
+
+static void sim_ins_special_sop33(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.shamt) {
+ case MIPS_SOP33_DIVU:
+ sim->reg[ins.rd] = sim->reg[ins.rs] / sim->reg[ins.rt];
+ break;
+
+ case MIPS_SOP33_MODU:
+ sim->reg[ins.rd] = sim->reg[ins.rs] % sim->reg[ins.rt];
+ break;
+
+ default:
+ sim_dump(sim, "unknown sop33 funct (0b%06b)", ins.shamt);
+ }
+}
+
+static void sim_ins_special(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.funct) {
+ case MIPS_FUNCT_ADD:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] +
+ (int32_t)sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_ADDU:
+ sim->reg[ins.rd] = sim->reg[ins.rs] + sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_AND:
+ sim->reg[ins.rd] = sim->reg[ins.rs] & sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_SOP30:
+ sim_ins_special_sop30(sim, ins);
+ break;
+
+ case MIPS_FUNCT_SOP31:
+ sim_ins_special_sop31(sim, ins);
+ break;
+
+ case MIPS_FUNCT_SOP32:
+ sim_ins_special_sop32(sim, ins);
+ break;
+
+ case MIPS_FUNCT_SOP33:
+ sim_ins_special_sop33(sim, ins);
+ break;
+
+ case MIPS_FUNCT_JALR:
+ sim->reg[MIPS_REG_RA] = sim->pc;
+ /* fall through */
+ case MIPS_FUNCT_JR:
+ sim->pc = sim->reg[ins.rs];
+ break;
+
+ case MIPS_FUNCT_MFHI:
+ sim->reg[ins.rd] = sim->hi;
+ break;
+
+ case MIPS_FUNCT_MFLO:
+ sim->reg[ins.rd] = sim->low;
+ break;
+
+ case MIPS_FUNCT_MTHI:
+ sim->hi = sim->reg[ins.rd];
+ break;
+
+ case MIPS_FUNCT_MTLO:
+ sim->low = sim->reg[ins.rd];
+ break;
+
+ case MIPS_FUNCT_SLL:
+ sim->reg[ins.rd] = sim->reg[ins.rt] << ins.shamt;
+ break;
+
+ case MIPS_FUNCT_SLLV:
+ sim->reg[ins.rd] = sim->reg[ins.rt] << sim->reg[ins.rs];
+ break;
+
+ case MIPS_FUNCT_SLT:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] < (int32_t)sim->reg[ins.rt] ? 1 : 0;
+ break;
+
+ case MIPS_FUNCT_SLTU:
+ sim->reg[ins.rd] = sim->reg[ins.rs] < sim->reg[ins.rt] ? 1 : 0;
+ break;
+
+ case MIPS_FUNCT_SRA:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rt] >> ins.shamt;
+ break;
+
+ case MIPS_FUNCT_SRAV:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rt] >> sim->reg[ins.rs];
+ break;
+
+ case MIPS_FUNCT_SRL:
+ sim->reg[ins.rd] = sim->reg[ins.rt] >> ins.shamt;
+ break;
+
+ case MIPS_FUNCT_SRLV:
+ sim->reg[ins.rd] = sim->reg[ins.rt] >> sim->reg[ins.rs];
+ break;
+
+ case MIPS_FUNCT_SUB:
+ sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] -
+ (int32_t)sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_SUBU:
+ sim->reg[ins.rd] = sim->reg[ins.rs] - sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_SYSCALL:
+ sim->reg[MIPS_REG_V0] = syscall(
+ sim->reg[MIPS_REG_V0],
+ sim->reg[MIPS_REG_A0],
+ sim->reg[MIPS_REG_A1],
+ sim->reg[MIPS_REG_A2],
+ sim->reg[MIPS_REG_A3]
+ );
+ break;
+
+ case MIPS_FUNCT_OR:
+ sim->reg[ins.rd] = sim->reg[ins.rs] | sim->reg[ins.rt];
+ break;
+
+ case MIPS_FUNCT_NOR:
+ sim->reg[ins.rd] = !(sim->reg[ins.rs] | sim->reg[ins.rt]);
+ break;
+
+ case MIPS_FUNCT_XOR:
+ sim->reg[ins.rd] = sim->reg[ins.rs] ^ sim->reg[ins.rt];
+ break;
+
+ default:
+ sim_dump(sim, "unknown funct (0b%05b)", ins.funct);
+ }
+}
+
+static void sim_ins_regimm(struct simulator *sim,
+ union mips_instruction_data ins)
+{
+ switch (ins.bfunct) {
+ case MIPS_FUNCT_BGEZAL:
+ case MIPS_FUNCT_BGEZALL:
+ sim->reg[MIPS_REG_RA] = sim->pc;
+ /* fall through */
+ case MIPS_FUNCT_BGEZ:
+ case MIPS_FUNCT_BGEZL:
+ if ((int32_t)sim->reg[ins.rs] >= 0)
+ sim->pc += ins.offset << 2;
+ break;
+
+ case MIPS_FUNCT_BLTZAL:
+ case MIPS_FUNCT_BLTZALL:
+ sim->reg[MIPS_REG_RA] = sim->pc;
+ /* fall through */
+ case MIPS_FUNCT_BLTZ:
+ case MIPS_FUNCT_BLTZL:
+ if ((int32_t)sim->reg[ins.rs] < 0)
+ sim->pc += ins.offset << 2;
+ break;
+
+ default:
+ sim_dump(sim, "unknown bfunct (0b%06b)", ins.bfunct);
+ }
+}
+
+void sim_ins(struct simulator *sim, uint32_t raw)
+{
+ // get ins parts
+ union mips_instruction_data ins = {
+ .raw = B32(raw)
+ };
+
+ // reset zero reg
+ sim->reg[MIPS_REG_ZERO] = 0;
+
+ switch (ins.op) {
+ case MIPS_OP_SPECIAL:
+ sim_ins_special(sim, ins);
+ break;
+
+ case MIPS_OP_REGIMM:
+ sim_ins_regimm(sim, ins);
+ break;
+
+ case MIPS_OP_ADDI:
+ sim->reg[ins.rt] = (int32_t)sim->reg[ins.rs] +
+ (int16_t) ins.immd;
+ break;
+
+ case MIPS_OP_ADDIU:
+ sim->reg[ins.rt] = sim->reg[ins.rs] + ins.immd;
+ break;
+
+ case MIPS_OP_ANDI:
+ sim->reg[ins.rt] = sim->reg[ins.rs] & ins.immd;
+ break;
+
+ case MIPS_OP_BALC:
+ sim->reg[MIPS_REG_RA] = sim->pc;
+ /* fall through */
+ case MIPS_OP_BC:
+ sim->pc += ins.offs26 << 2;
+ break;
+
+ case MIPS_OP_BEQ:
+ case MIPS_OP_BEQL:
+ if (sim->reg[ins.rs] == sim->reg[ins.rt])
+ sim->pc += ins.offset << 2;
+ break;
+
+ case MIPS_OP_BGTZ:
+ case MIPS_OP_BGTZL:
+ if ((int32_t)sim->reg[ins.rs] <= 0)
+ sim->pc += ins.offset << 2;
+ break;
+
+ case MIPS_OP_BLEZ:
+ case MIPS_OP_BLEZL:
+ if ((int32_t)sim->reg[ins.rs] <= 0)
+ sim->pc += ins.offset << 2;
+ break;
+
+ case MIPS_OP_BNE:
+ case MIPS_OP_BNEL:
+ if (sim->reg[ins.rs] != sim->reg[ins.rt])
+ sim->pc += ins.offset << 2;
+ break;
+
+ case MIPS_OP_JAL:
+ sim->reg[MIPS_REG_RA] = sim->pc;
+ /* fall through */
+ case MIPS_OP_J:
+ sim->pc = ins.target << 2;
+ break;
+
+ case MIPS_OP_LB:
+ sim->reg[ins.rt] = * (int8_t *) (uintptr_t) (sim->reg[ins.rs]
+ + ins.offset);
+ break;
+
+ case MIPS_OP_LBU:
+ sim->reg[ins.rt] = * (uint8_t *) (uintptr_t) (sim->reg[ins.rs]
+ + ins.offset);
+ break;
+
+ case MIPS_OP_LH:
+ sim->reg[ins.rt] = * (int16_t *) (uintptr_t) (sim->reg[ins.rs]
+ + ins.offset);
+ break;
+
+ case MIPS_OP_LHU:
+ sim->reg[ins.rt] = * (uint16_t *) (uintptr_t) (sim->reg[ins.rs]
+ + ins.offset);
+ break;
+
+ case MIPS_OP_LUI:
+ sim->reg[ins.rt] = ins.immd << 16;
+ break;
+
+ case MIPS_OP_LW:
+ sim->reg[ins.rt] = * (uint32_t *) (uintptr_t) (sim->reg[ins.rs]
+ + ins.offset);
+ break;
+
+ case MIPS_OP_SB:
+ * (uint8_t *) (uintptr_t) (sim->reg[ins.rs] +
+ + ins.offset) = sim->reg[ins.rt];
+ break;
+
+ case MIPS_OP_SH:
+ * (uint16_t *) (uintptr_t) (sim->reg[ins.rs] +
+ ins.offset) = sim->reg[ins.rt];
+ break;
+
+ case MIPS_OP_SW:
+ * (uint32_t *) (uintptr_t) (sim->reg[ins.rs] +
+ ins.offset) = sim->reg[ins.rt];
+ break;
+
+ case MIPS_OP_SLTI:
+ sim->reg[ins.rt] = (int32_t)sim->reg[ins.rs] <
+ (int16_t)ins.immd ? 1 : 0;
+ break;
+
+ case MIPS_OP_SLTIU:
+ sim->reg[ins.rt] = sim->reg[ins.rs] < ins.immd ? 1 : 0;
+ break;
+
+ case MIPS_OP_ORI:
+ sim->reg[ins.rt] = sim->reg[ins.rs] | ins.immd;
+ break;
+
+ case MIPS_OP_XORI:
+ sim->reg[ins.rt] = sim->reg[ins.rs] ^ ins.immd;
+ break;
+
+ default:
+ sim_dump(sim, "unknown op code (0b%05b)", ins.op);
+ }
+}
+
diff --git a/msim/load.c b/msim/load.c
new file mode 100644
index 0000000..77b62ed
--- /dev/null
+++ b/msim/load.c
@@ -0,0 +1,226 @@
+#include <merror.h>
+#include <elf.h>
+#include <melf.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "sim.h"
+
+#define SEC_ALIGN 0x1000
+
+struct load_state {
+ FILE *file;
+ int fd;
+ uint32_t file_sz;
+
+ Elf32_Phdr *phdr;
+ uint32_t phdr_len;
+ Elf32_Ehdr ehdr;
+};
+
+static int assert_ehdr(const void *const given, const void *const assert,
+ size_t size)
+{
+ if (memcmp(given, assert, size) == 0)
+ return M_SUCCESS;
+ ERROR("invalid elf ehdr");
+ return M_ERROR;
+}
+
+static int load_ehdr(struct simulator *sim, struct load_state *state)
+{
+ Elf32_Ehdr ehdr;
+ fseek(state->file, 0, SEEK_SET);
+ if (fread(&ehdr, sizeof(Elf32_Ehdr), 1, state->file) != 1) {
+ ERROR("cannot load ehdr");
+ return M_ERROR;
+ }
+
+ /**
+ * Compare each "static" value in the ehdr and make sure it
+ * is what it should be. If not throw and error and eventually
+ * return.
+ */
+
+ Elf32_Ehdr baseline = MIPS_ELF_EHDR;
+ baseline.e_type = B16(ET_EXEC);
+
+ #define EHDR_ASSERT(name, size) \
+ if (res == M_SUCCESS) \
+ res |= assert_ehdr(&baseline.e_##name, \
+ &ehdr.e_##name, size) \
+
+ int res = 0;
+ EHDR_ASSERT(ident, EI_NIDENT);
+ EHDR_ASSERT(type, sizeof(Elf32_Half));
+ EHDR_ASSERT(machine, sizeof(Elf32_Half));
+ EHDR_ASSERT(version, sizeof(Elf32_Word));
+ EHDR_ASSERT(flags, sizeof(Elf32_Word));
+ EHDR_ASSERT(ehsize, sizeof(Elf32_Half));
+ EHDR_ASSERT(phentsize, sizeof(Elf32_Half));
+ EHDR_ASSERT(shentsize, sizeof(Elf32_Half));
+
+ #undef EHDR_ASSERT
+
+ if (res)
+ return M_ERROR;
+
+ state->ehdr = ehdr;
+ sim->entry = B32(ehdr.e_entry);
+
+ return M_SUCCESS;
+}
+
+static int load_phdr(struct load_state *state)
+{
+ Elf32_Phdr *phdr = NULL;
+
+ uint32_t off = B32(state->ehdr.e_phoff);
+ uint16_t len = B16(state->ehdr.e_phnum);
+
+ if (BOUND_CHK(state->file_sz, len, off)) {
+ ERROR("elf phdr location invalid");
+ return M_ERROR;
+ }
+
+ phdr = malloc(sizeof(Elf32_Phdr) * len);
+ if (phdr == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ fseek(state->file, off, SEEK_SET);
+ if (fread(phdr, sizeof(Elf32_Phdr), len, state->file) != len) {
+ free(phdr);
+ PERROR("cannot read phdr");
+ return M_ERROR;
+ }
+
+ state->phdr = phdr;
+ state->phdr_len = len;
+ return M_SUCCESS;
+}
+
+static int load_segment(struct simulator *sim,
+ struct load_state *state, Elf32_Phdr *hdr)
+{
+ uint32_t off = B32(hdr->p_offset);
+ uint32_t len = B32(hdr->p_filesz);
+ uint32_t is_text = B32(hdr->p_flags) & PF_X;
+
+ if (BOUND_CHK(state->file_sz, len, off)) {
+ ERROR("segment location invalid");
+ return M_ERROR;
+ }
+
+ uintptr_t addr = 0;
+ uint32_t add = 0;
+
+ if (len % SEC_ALIGN) {
+ add = SEC_ALIGN - (len % SEC_ALIGN);
+ len += add;
+ }
+
+ if (is_text) {
+ addr = sim->text_max;
+ sim->text_max += len;
+ } else {
+ addr = sim->data_max;
+ sim->data_max += len;
+ }
+
+ bool read = B32(hdr->p_flags) & PF_R;
+ bool write = B32(hdr->p_flags) & PF_W;
+
+ uint32_t prot = 0;
+ if (read)
+ prot |= PROT_READ;
+ if (write)
+ prot |= PROT_WRITE;
+
+ void *res = mmap((void*)addr, len, prot, MAP_PRIVATE | MAP_FIXED,
+ state->fd, off);
+
+ if ((uintptr_t)res != addr) {
+ PERROR("failed to map executable");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+static int load_memory(struct simulator *sim, struct load_state *state)
+{
+ uint32_t base = 0;
+
+ for (uint32_t i = 0; i < state->phdr_len; i++) {
+ Elf32_Phdr *hdr = NULL;
+ uint32_t min = UINT32_MAX;
+
+ if (B32(state->phdr[i].p_filesz) < 1)
+ continue;
+
+ // we need to load segments in order
+ for (uint32_t j = 0; j < state->phdr_len; j++) {
+ Elf32_Phdr *temp = &state->phdr[j];
+ uint32_t off = B32(temp->p_offset);
+ uint32_t len = B32(temp->p_filesz);
+
+ if (len < 1)
+ continue;
+
+ if (off <= base)
+ continue;
+
+ if (off >= min)
+ continue;
+
+ min = off;
+ hdr = temp;
+ }
+
+ base = min;
+
+ if (hdr == NULL) {
+ ERROR("invalid elf phdr");
+ return M_ERROR;
+ }
+
+ if (load_segment(sim, state, hdr))
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+int sim_load_file(struct simulator *sim) {
+ struct load_state state;
+
+ state.file = fopen(sim->args->executable, "r");
+ if (state.file == NULL) {
+ PERROR("cannot read '%s'", state.file);
+ return M_ERROR;
+ }
+ state.fd = fileno(state.file);
+
+ // get filesize
+ fseek(state.file, 0, SEEK_END);
+ state.file_sz = ftell(state.file);
+ state.phdr = NULL;
+
+ int res = M_SUCCESS;
+ if (res == M_SUCCESS)
+ res = load_ehdr(sim, &state);
+ if (res == M_SUCCESS)
+ res = load_phdr(&state);
+ if (res == M_SUCCESS)
+ res = load_memory(sim, &state);
+
+ if (state.phdr)
+ free(state.phdr);
+ fclose(state.file);
+
+ return res;
+}
diff --git a/msim/main.c b/msim/main.c
index 7afb34e..f8a0999 100644
--- a/msim/main.c
+++ b/msim/main.c
@@ -1,5 +1,73 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <merror.h>
+#include <signal.h>
-int main(void) {
- // NOT YET IMPLEMENTED
- return 1;
+#include "fault.h"
+#include "sim.h"
+
+static void help(void) {
+ printf("usage: msim [options] executable\n\n");
+ printf("options:\n");
+ printf("\t-h\t\tprints this help messaage\n");
+ printf("\t-c\t\tdisable runtime memory checks (allows segfault)\n");
+ printf("\t-d\t\truns the debugger\n");
+}
+
+int main(int argc, char **argv) {
+
+ struct simulator sim;
+ struct simulator_args args = {
+ .executable = NULL,
+ .debug = false,
+ .memchk = true,
+ };
+
+ int c;
+
+ while ((c = getopt(argc, argv, "hcd")) != 1) {
+ switch (c) {
+ case 'h':
+ help();
+ return M_SUCCESS;
+ case 'c':
+ args.memchk = false;
+ break;
+ case 'd':
+ args.debug = true;
+ break;
+ case '?':
+ return M_ERROR;
+ default:
+ goto next;
+ }
+ }
+
+next:
+ if (optind < argc - 1) {
+ ERROR("too many executables passed");
+ return M_ERROR;
+ }
+
+ if (optind >= argc) {
+ ERROR("no executable passed");
+ return M_ERROR;
+ }
+
+ /// set file name
+ args.executable = argv[optind];
+
+ /// create handlers
+ init_handlers(&sim);
+
+ /// init
+ if (sim_init(&sim, &args))
+ return M_ERROR;
+
+ /* _Noreturn */
+ if (args.debug)
+ sim_debug(&sim);
+ else
+ sim_run(&sim);
}
diff --git a/msim/sim.c b/msim/sim.c
new file mode 100644
index 0000000..9e0d518
--- /dev/null
+++ b/msim/sim.c
@@ -0,0 +1,93 @@
+#include <merror.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "sim.h"
+#include "melf.h"
+
+void sim_dump_reg(struct simulator *sim)
+{
+ const char *names[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1",
+ "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3",
+ "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp",
+ "fp", "ra"
+ };
+
+ for (uint32_t i = 0; i < 8; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ if (j != 0)
+ fputc(' ', stderr);
+ int idx = i+j*8;
+ fprintf(stderr, "$%s:\t0x%08x", names[idx], sim->reg[idx]);
+ }
+ fputc('\n', stderr);
+ }
+}
+
+__attribute__((format(printf, 2, 3)))
+_Noreturn void sim_dump(struct simulator *sim, const char *format, ...) {
+
+ va_list list;
+ va_start(list, format);
+
+ // header
+ fprintf(stderr, "\n\t\t!!! An exception has occurred !!!\n\n");
+ fprintf(stderr, "\033[31merror: \033[0m");
+ vfprintf(stderr, format, list);
+ fputc('\n', stderr);
+
+ // pc
+ uint32_t pc = sim->current_pc;
+ fprintf(stderr, "pc: 0x%08x\n", pc);
+
+ if (pc >= sim->text_min && pc < sim->text_max)
+ fprintf(stderr, "ins: 0x%08x\n", B32(* (uint32_t *) (uintptr_t) pc));
+
+ // registers
+ fprintf(stderr, "registers:\n");
+ sim_dump_reg(sim);
+
+ exit(1);
+}
+
+void sim_step(struct simulator *sim)
+{
+ if (sim->pc < sim->text_min || sim->pc >= sim->text_max)
+ sim_dump(sim, "program counter reached non executable memory");
+
+ uint32_t ins = * (uint32_t *) (uintptr_t) sim->pc;
+ sim->pc += 4;
+ sim_ins(sim, ins);
+ sim->current_pc = sim->pc;
+}
+
+_Noreturn void sim_run(struct simulator *sim)
+{
+ sim->pc = sim->entry;
+ sim->current_pc = sim->pc;
+ while (1)
+ sim_step(sim);
+}
+
+int sim_init(struct simulator *sim, struct simulator_args *args) {
+ memset(sim->reg, 0, sizeof(uint32_t) * 32);
+ sim->data_max = MEM_DATA_ADDR;
+ sim->data_min = MEM_DATA_ADDR;
+ sim->text_max = MEM_TEXT_ADDR;
+ sim->text_min = MEM_TEXT_ADDR;
+ sim->args = args;
+
+ // load executable
+ if (sim_load_file(sim))
+ return M_ERROR;
+
+ // check entry
+ if (sim->entry < sim->text_min || sim->entry >= sim->text_max) {
+ ERROR("elf entrypoint invalid");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
diff --git a/msim/sim.h b/msim/sim.h
new file mode 100644
index 0000000..517babd
--- /dev/null
+++ b/msim/sim.h
@@ -0,0 +1,87 @@
+/* Copyright (c) 2024 Freya Murphy */
+
+#ifndef __SIM_H__
+#define __SIM_H__
+
+#include <elf.h>
+#include <stdint.h>
+
+/// arguments
+struct simulator_args {
+ char *executable;
+ bool memchk;
+ bool debug;
+};
+
+
+// when loading the executable, we need to bounds check to
+// make sure its within the file size
+//
+// this checks that
+// 1. the end is in the file
+// 2. the off and len doesnt integer overflow
+#define BOUND_CHK(size, len, off) \
+ ((off) > UINT32_MAX - (len) || (off) + (len) > (size))
+
+///
+/// data must start at these
+/// addresses or the executable is
+/// invalid
+///
+#define MEM_TEXT_ADDR 0x00400000
+#define MEM_DATA_ADDR 0x10000000
+
+struct simulator {
+ struct simulator_args *args;
+
+
+ /// the registers
+ uint32_t reg[32];
+
+ /// the program counter
+ uint32_t pc;
+
+ /// the current executing pc
+ uint32_t current_pc;
+
+ /// low and hi
+ uint32_t low;
+ uint32_t hi;
+
+ /// memory address bounds
+ uint32_t data_min;
+ uint32_t data_max;
+ uint32_t text_min;
+ uint32_t text_max;
+
+ // entrypoint
+ uint32_t entry;
+};
+
+/* initalize a simulator */
+int sim_init(struct simulator *sim, struct simulator_args *args);
+
+/* load a file into a simulator */
+int sim_load_file(struct simulator *sim);
+
+/* execute and instruction */
+void sim_ins(struct simulator *sim, uint32_t ins);
+
+/* execute the next instruction in the simulator */
+void sim_step(struct simulator *sim);
+
+/* run the simulator with the loaded state */
+_Noreturn void sim_run(struct simulator *sim);
+
+/* run the debugger for a simulator */
+_Noreturn void sim_debug(struct simulator *sim);
+
+/* panics the simulator */
+__attribute__((format(printf, 2, 3)))
+_Noreturn void sim_dump(struct simulator *sim, const char *format, ...);
+
+/* dumps registers */
+void sim_dump_reg(struct simulator *sim);
+
+#endif /* __SIM_H__ */
+