aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin Borner2023-01-28 22:29:48 +0100
committerMarvin Borner2023-01-28 22:29:48 +0100
commitde450bfb4354f716fb43fae69d90ed513068d10b (patch)
treeb474219ae3baaeb8a075e6fbb5c57c3857faa588
parent75bb42663294b388377178b7257c6f8c0f787032 (diff)
Rewrite, bootstrap
Rewriting without KVM, huge goals, won't finish, just for fun.
-rw-r--r--.gitignore1
-rw-r--r--Makefile28
-rw-r--r--README.md3
-rw-r--r--inc/cpu.h57
-rw-r--r--inc/kvm.h9
-rw-r--r--inc/linux.h6
-rw-r--r--inc/load.h8
-rw-r--r--inc/mem.h13
-rw-r--r--makefile50
-rw-r--r--readme.md5
-rw-r--r--src/cpu.c206
-rw-r--r--src/kvm.c344
-rw-r--r--src/linux.c41
-rw-r--r--src/load.c59
-rw-r--r--src/main.c70
-rw-r--r--src/mem.c58
-rw-r--r--test.asm12
-rw-r--r--tests/asm/yay.asm17
18 files changed, 530 insertions, 457 deletions
diff --git a/.gitignore b/.gitignore
index 3155dd1..01171d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
build/
.idea/
+.cache/
compile_commands.json
.clang-format
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 391e641..0000000
--- a/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-SOURCEDIR = $(PWD)/src
-INCDIR = $(PWD)/inc
-BUILDDIR = $(PWD)/build
-SOURCES = $(wildcard $(SOURCEDIR)/*.c)
-OBJS = $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
-
-CC = gcc
-WARNINGS = -Wall -Wextra -Wshadow -Wpointer-arith -Wwrite-strings -Wredundant-decls -Wnested-externs -Wformat=2 -Wmissing-declarations -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -Wswitch-default -Wswitch-enum -Wlogical-op -Wunreachable-code -Wundef -Wold-style-definition -Wvla -pedantic
-DEBUG = -fsanitize=undefined -fsanitize=address -fstack-protector-strong -ggdb3 -g3 -g -s -Og
-CFLAGS = -Ofast $(WARNINGS) -I$(INCDIR) $(DEBUG)
-
-all: $(OBJS)
- @$(CC) -o $(BUILDDIR)/out $(CFLAGS) $^
-
-clean:
- @$(RM) -rf $(BUILDDIR)
-
-run: clean all sync
- @nasm test.asm -o $(BUILDDIR)/test
- @$(BUILDDIR)/out
-
-sync:
- @make --always-make --dry-run | grep -wE 'gcc|g\+\+' | grep -w '\-c' | jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$$").string[1:]}]' >compile_commands.json
- @ctags -R --exclude=.git --exclude=build .
-
-$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
- @mkdir -p $(BUILDDIR)
- @$(CC) -c -o $@ $(CFLAGS) $<
diff --git a/README.md b/README.md
deleted file mode 100644
index 2e73a7f..0000000
--- a/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Simsalasim
-
-It's an x86 simulator, nothing more.
diff --git a/inc/cpu.h b/inc/cpu.h
new file mode 100644
index 0000000..1a9a989
--- /dev/null
+++ b/inc/cpu.h
@@ -0,0 +1,57 @@
+#ifndef CPU_H
+#define CPU_H
+
+#include <stdint.h>
+
+#define ERR 0
+#define OK 1
+
+enum registers {
+ RAX,
+ RCX,
+ RDX,
+ RBX,
+ RSP,
+ RBP,
+ RSI,
+ RDI,
+ R8,
+ R9,
+ R10,
+ R11,
+ R12,
+ R13,
+ R14,
+ R15,
+ REGISTERS_COUNT,
+ AL = RAX,
+ CL = RCX,
+ DL = RDX,
+ BL = RBX,
+ SPL = RSP,
+ BPL = RBP,
+ SIL = RSI,
+ DIL = RDI,
+ R8B = R8,
+ R9B = R9,
+ R10B = R10,
+ R11B = R11,
+ R12B = R12,
+ R13B = R13,
+ R14B = R14,
+ R15B = R15,
+ AH = AL + 4,
+ CH = CL + 4,
+ DH = DL + 4,
+ BH = BL + 4,
+ SPH = SPL + 4,
+ BPH = BPL + 4,
+ SIH = SIL + 4,
+ DIH = DIL + 4
+};
+
+void *cpu_get_reg(uint8_t reg);
+void cpu_set_reg(uint8_t reg, uint64_t val);
+void cpu_exec(const char *path);
+
+#endif
diff --git a/inc/kvm.h b/inc/kvm.h
deleted file mode 100644
index 92dbe98..0000000
--- a/inc/kvm.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef KVM_H
-#define KVM_H
-
-int kvm_init(void);
-int kvm_load(long addr, long data, int size);
-void kvm_exec(void);
-void kvm_destroy(void);
-
-#endif
diff --git a/inc/linux.h b/inc/linux.h
new file mode 100644
index 0000000..2d45f64
--- /dev/null
+++ b/inc/linux.h
@@ -0,0 +1,6 @@
+#ifndef LINUX_H
+#define LINUX_H
+
+int linux_call(void);
+
+#endif
diff --git a/inc/load.h b/inc/load.h
new file mode 100644
index 0000000..4b215cc
--- /dev/null
+++ b/inc/load.h
@@ -0,0 +1,8 @@
+#ifndef LOAD_H
+#define LOAD_H
+
+#include <mem.h>
+
+vaddr load(const char *path);
+
+#endif
diff --git a/inc/mem.h b/inc/mem.h
new file mode 100644
index 0000000..e2d7a18
--- /dev/null
+++ b/inc/mem.h
@@ -0,0 +1,13 @@
+#ifndef MEM_H
+#define MEM_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+typedef uint64_t vaddr;
+
+void *mem_alloc(size_t size, vaddr virt);
+void *mem_phys(vaddr virt);
+void mem_free_all(void);
+
+#endif
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..a416726
--- /dev/null
+++ b/makefile
@@ -0,0 +1,50 @@
+# TODO: I think I'm using make wrong
+SOURCEDIR = $(PWD)/src
+TESTSDIR = $(PWD)/tests
+INCDIR = $(PWD)/inc
+BUILDDIR = $(PWD)/build
+SOURCES = $(wildcard $(SOURCEDIR)/*.c)
+OBJS = $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/%.o, $(SOURCES))
+
+CTESTS = $(wildcard $(TESTSDIR)/c/*.c)
+CTESTSOBJS = $(patsubst $(TESTSDIR)/c/%.c, $(BUILDDIR)/test_c_%.o, $(CTESTS))
+ASMTESTS = $(wildcard $(TESTSDIR)/asm/*.asm)
+ASMTESTSOBJS = $(patsubst $(TESTSDIR)/asm/%.asm, $(BUILDDIR)/test_asm_%.o, $(ASMTESTS))
+
+CC = gcc
+AS = nasm
+LD = ld
+WARNINGS = -Wall -Wextra -Wshadow -Wpointer-arith -Wwrite-strings -Wredundant-decls -Wnested-externs -Wformat=2 -Wmissing-declarations -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -Wswitch-default -Wswitch-enum -Wlogical-op -Wunreachable-code -Wundef -Wold-style-definition -Wvla -pedantic -Wno-pointer-arith
+DEBUG = -fsanitize=undefined -fsanitize=address -fsanitize=leak -fstack-protector-strong -s
+CFLAGS = -Ofast $(WARNINGS) -I$(INCDIR) $(DEBUG)
+ASFLAGS = -O2 -f elf64 # TODO: Use -O0
+
+all: out tests
+
+out: $(OBJS)
+ @$(CC) -o $(BUILDDIR)/out $(CFLAGS) $^
+
+tests: $(CTESTSOBJS) $(ASMTESTSOBJS)
+
+$(BUILDDIR)/test_asm_%.o: $(TESTSDIR)/asm/%.asm
+ @$(AS) $(ASFLAGS) $< -o $@
+ @$(LD) $@ -o $(basename $@)
+
+$(BUILDDIR)/test_c_%.o: $(TESTSDIR)/c/%.c
+ @$(CC) $< -o $(basename $@)
+
+clean:
+ @$(RM) -rf $(BUILDDIR)/*
+
+run: all sync
+ @$(BUILDDIR)/out $(BUILDDIR)/test_asm_yay
+
+sync:
+ @make --always-make --dry-run | grep -wE 'gcc|g\+\+' | grep -w '\-c' | jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$$").string[1:]}]' >compile_commands.json
+ @ctags -R --exclude=.git --exclude=build .
+
+$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
+ @mkdir -p $(BUILDDIR)
+ @$(CC) -c -o $@ $(CFLAGS) $<
+
+.PHONY: all out tests clean run sync
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..563b083
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,5 @@
+# Simsalasim
+
+> A time-warping x86 simulator.
+
+Abracadabra your program shall be debugged! *whoosh*
diff --git a/src/cpu.c b/src/cpu.c
new file mode 100644
index 0000000..9c7c2a2
--- /dev/null
+++ b/src/cpu.c
@@ -0,0 +1,206 @@
+#include <cpu.h>
+#include <load.h>
+#include <log.h>
+#include <linux.h>
+#include <mem.h>
+
+#define MOD() ((modrm & 0xc0) >> 6)
+#define RM() (modrm & 0x07)
+#define DISP32() (U32())
+#define DISP8() (S8())
+#define REG() ((modrm & 0x38) >> 3)
+#define ADDR() \
+ __extension__({ \
+ uint32_t _ret = 0; \
+ if (MOD() == 0) { \
+ if (RM() == 4) \
+ errln("TODO."); \
+ else if (RM() == 5) \
+ _ret = DISP32(); \
+ else \
+ _ret = GET_R32(RM()); \
+ } else if (MOD() == 1) { \
+ if (RM() == 4) \
+ errln("TODO."); \
+ else \
+ _ret = GET_R32(RM()) + DISP8(); \
+ } else { \
+ if (RM() == 4) \
+ errln("TODO."); \
+ else \
+ _ret = GET_R32(RM()) + DISP32(); \
+ } \
+ _ret; \
+ })
+
+#define U8() (*(uint8_t *)mem_phys(rip++))
+#define S8() (*(int8_t *)mem_phys(rip++))
+#define U16() \
+ __extension__({ \
+ uint16_t ret = *(uint16_t *)mem_phys(rip); \
+ rip += 2; \
+ ret; \
+ })
+#define S16() \
+ __extension__({ \
+ int16_t ret = *(int16_t *)mem_phys(rip); \
+ rip += 2; \
+ ret; \
+ })
+#define U32() \
+ __extension__({ \
+ uint32_t ret = *(uint32_t *)mem_phys(rip); \
+ rip += 4; \
+ ret; \
+ })
+#define S32() \
+ __extension__({ \
+ int32_t ret = *(int32_t *)mem_phys(rip); \
+ rip += 4; \
+ ret; \
+ })
+#define U64() \
+ __extension__({ \
+ uint64_t ret = *(uint64_t *)mem_phys(rip); \
+ rip += 8; \
+ ret; \
+ })
+#define S64() \
+ __extension__({ \
+ int64_t ret = *(int64_t *)mem_phys(rip); \
+ rip += 8; \
+ ret; \
+ })
+
+#define GET_R8(r) (r < 8 ? regs[r] & 0xff : (regs[r - 8] >> 8) & 0xff)
+#define GET_R32(r) ((uint32_t)regs[r])
+#define GET_R64(r) ((uint64_t)regs[r])
+#define SET_R8(r, v) \
+ __extension__({ \
+ if (r < 8) \
+ regs[r] = (regs[r] & 0xffffff00) | (uint32_t)v; \
+ else \
+ regs[r - 4] = (regs[r] = (regs[r] & 0xffff00ff) | \
+ ((uint32_t)v << 8)) \
+ })
+#define SET_R32(r, v) (regs[r] = v)
+#define SET_R64(r, v) (regs[r] = v)
+
+#define GET_RM8() (MOD() == 3 ? GET_R8(RM()) : *(uint8_t *)mem_phys(ADDR()))
+#define GET_RM32() (MOD() == 3 ? GET_R32(RM()) : *(uint32_t *)mem_phys(ADDR()))
+
+static uint64_t rip = 0;
+static int (*pos_instructions[256])(void);
+static int (*neg_instructions[256])(void);
+static uint64_t regs[REGISTERS_COUNT] = { 0 };
+
+static void print_state(void)
+{
+ logln("rip=%x, rax=%x, rcx=%x, rdx=%x, rbx=%x, rsp=%x, rbp=%x, rsi=%x, rdi=%x,\nr8=%x, r9=%x, r10=%x, r11=%x, r12=%x, r13=%x, r14=%x, r15=%x",
+ rip, regs[RAX], regs[RCX], regs[RDX], regs[RBX], regs[RSP],
+ regs[RBP], regs[RSI], regs[RSI], regs[RDI], regs[R8], regs[R9],
+ regs[R10], regs[R11], regs[R12], regs[R13], regs[R14], regs[R15]);
+}
+
+static int pos_unknown_instruction(void)
+{
+ errln("unknown positive instruction at rip=%d", rip);
+ return ERR;
+}
+
+static int neg_unknown_instruction(void)
+{
+ errln("unknown negative instruction at rip=%d", rip);
+ return ERR;
+}
+
+static int pos_opcode(void)
+{
+ uint8_t op = U8();
+ // TODO: is_*, j*
+ if (op == 0x05) {
+ return linux_call();
+ }
+ return ERR;
+}
+
+static int pos_mov_r32_rm32(void)
+{
+ uint8_t modrm = U8();
+ SET_R32(REG(), GET_RM32());
+ return OK;
+}
+
+static int pos_mov_r32_imm32(void)
+{
+ rip--;
+ uint8_t reg = U8() - 0xb8;
+ SET_R32(reg, U32());
+ return OK;
+}
+
+static int pos_nop(void)
+{
+ return OK;
+}
+
+static int pos_int(void)
+{
+ uint8_t op = U8();
+ if (op == 0x80) {
+ // Linux syscall
+ // TODO: Support legacy syscalls (numbers are different)
+ return ERR;
+ }
+ return ERR;
+}
+
+static void initialize(void)
+{
+ for (int i = 0; i < 256; i++) {
+ pos_instructions[i] = pos_unknown_instruction;
+ neg_instructions[i] = neg_unknown_instruction;
+ }
+ pos_instructions[0x0f] = pos_opcode;
+ pos_instructions[0x8b] = pos_mov_r32_rm32;
+ pos_instructions[0x90] = pos_nop;
+ pos_instructions[0xcd] = pos_int;
+ /* for (int i = 0; i < 8; i++) */
+ /* pos_instructions[0xb0+i] = pos_mov_r8_imm8; */
+ for (int i = 0; i < 8; i++)
+ pos_instructions[0xb8 + i] = pos_mov_r32_imm32;
+}
+
+void *cpu_get_reg(uint8_t reg)
+{
+ return &regs[reg];
+}
+
+// TODO: Different signature?
+void cpu_set_reg(uint8_t reg, uint64_t val)
+{
+ regs[reg] = val;
+}
+
+void cpu_exec(const char *path)
+{
+ initialize();
+
+ vaddr addr = load(path);
+ if (!addr) {
+ errln("invalid entry address");
+ return;
+ }
+ rip = addr;
+
+ int ret = OK;
+ while (ret) {
+ uint8_t instr = U8();
+ if (!instr)
+ return;
+ logln("%x", instr);
+ ret = pos_instructions[instr]();
+ print_state();
+ }
+ errln("error while executing");
+}
diff --git a/src/kvm.c b/src/kvm.c
deleted file mode 100644
index f6ecf45..0000000
--- a/src/kvm.c
+++ /dev/null
@@ -1,344 +0,0 @@
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/kvm.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <kvm.h>
-#include <log.h>
-
-static int kvm = 0, vm = 0;
-static struct kvm_run *kvm_run;
-static void *memory;
-static size_t memory_size;
-
-struct {
- pthread_t thread;
- enum { OFFLINE = 0, RUNNING, ONLINE } state;
- int vcpu;
-} cpu[1] = { 0 };
-
-static int kvm_slot = 0;
-static int kvm_register(int flags, long guest_addr, long host_addr, size_t size)
-{
- struct kvm_userspace_memory_region region;
- region.slot = kvm_slot++;
- region.flags = flags;
- region.guest_phys_addr = guest_addr;
- region.memory_size = size;
- region.userspace_addr = host_addr;
-
- if (ioctl(vm, KVM_SET_USER_MEMORY_REGION, &region) < 0) {
- errln("Couldn't set memory region: %s", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static void kvm_signal(int number)
-{
- (void)number;
- logln("KVM signal");
-}
-
-static int kvm_init_cpu(int index)
-{
- /**
- * VCPU
- */
-
- cpu[index].vcpu = ioctl(vm, KVM_CREATE_VCPU, index);
- if (cpu[index].vcpu < 0) {
- errln("Couldn't create VCPU%d: %s", index, strerror(errno));
- return -1;
- }
-
- /**
- * KVM_RUN
- */
-
- int mmap_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, 0);
- if (mmap_size <= 0 || mmap_size & 0xfff) {
- errln("Invalid mmap_size: %d", mmap_size);
- return -1;
- }
-
- kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, cpu[index].vcpu, 0);
- if (kvm_run == MAP_FAILED) {
- errln("Couldn't map kvm_run");
- return -1;
- }
-
- /**
- * Registers
- */
-
- struct kvm_regs regs = { 0 };
- struct kvm_sregs sregs = { 0 };
-
- // Get registers
-
- if (ioctl(cpu[index].vcpu, KVM_GET_REGS, &regs) < 0) {
- errln("Couldn't get REGS of VCPU%d", index);
- return -1;
- }
-
- if (ioctl(cpu[index].vcpu, KVM_GET_SREGS, &sregs) < 0) {
- errln("Couldn't get SREGS of VCPU%d", index);
- return -1;
- }
-
- // Modify registers accordingly
-
- regs.rflags = 2;
- regs.rip = 0;
-
- sregs.cs.selector = 0;
- sregs.cs.base = 0;
-
- // Set registers
-
- if (ioctl(cpu[index].vcpu, KVM_SET_REGS, &regs) < 0) {
- errln("Couldn't set REGS of VCPU%d", index);
- return -1;
- }
-
- if (ioctl(cpu[index].vcpu, KVM_SET_SREGS, &sregs) < 0) {
- errln("Couldn't set SREGS of VCPU%d", index);
- return -1;
- }
-
- cpu[index].state = ONLINE;
- return 0;
-}
-
-int kvm_init(void)
-{
- kvm = open("/dev/kvm", O_RDWR);
- if (kvm < 0) {
- errln("KVM is required: %s", strerror(errno));
- return -1;
- }
-
- /**
- * Verify and create VM
- */
-
- int version = ioctl(kvm, KVM_GET_API_VERSION, 0);
- if (version != KVM_API_VERSION) {
- errln("Invalid KVM version: %d", version);
- return -1;
- }
-
- vm = ioctl(kvm, KVM_CREATE_VM, 0);
- if (vm < 0) {
- errln("Couldn't create VM: %s", strerror(errno));
- return -1;
- }
-
- // Verify max CPU count
-
- int max_cpus = ioctl(kvm, KVM_CHECK_EXTENSION, KVM_CAP_MAX_VCPUS);
- if (max_cpus < 0) {
- errln("Couldn't get CPU count: %s", strerror(errno));
- return -1;
- }
- assert((unsigned long)max_cpus >= sizeof(cpu) / sizeof(cpu[0]));
-
- /**
- * Memory
- */
-
- memory_size = 10 << 20;
- memory = mmap(NULL, memory_size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
- if (memory == MAP_FAILED) {
- errln("Couldn't map memory");
- return -1;
- }
-
- if (kvm_register(0, 0, (long)memory, memory_size) < 0)
- return -1;
-
- /**
- * CPUs
- */
-
- for (unsigned long i = 0; i < sizeof(cpu) / sizeof(cpu[0]); i++)
- if (kvm_init_cpu(i) < 0)
- return -1;
-
- /**
- * Okay!
- */
-
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = kvm_signal;
- sigaction(SIGALRM, &sa, NULL);
-
- return 0;
-}
-
-int kvm_load(long addr, long data, int size)
-{
- memcpy((char *)memory + addr, (void *)data, size);
- return 0;
-}
-
-static void *kvm_loop(void *arg)
-{
- long index = (long)arg;
- assert(cpu[index].state == ONLINE);
-
- while (cpu[index].state = RUNNING, ioctl(cpu[index].vcpu, KVM_RUN, 0) >= 0) {
- cpu[index].state = ONLINE;
-
- switch (kvm_run->exit_reason) {
- case KVM_EXIT_HLT:
- logln("Halting CPU");
- goto done;
- case KVM_EXIT_FAIL_ENTRY:
- errln("CPU failure");
- goto done;
- case KVM_EXIT_IO:
- logln("IO");
- break;
- case KVM_EXIT_MMIO:
- case KVM_EXIT_IRQ_WINDOW_OPEN:
- default:
- errln("Unknown KVM exit reason %d", kvm_run->exit_reason);
- goto done;
- }
- }
-
- errln("CPU can't be run: %s", strerror(errno));
-done:
- cpu[index].state = OFFLINE;
-
- logln("Done");
- pthread_exit(NULL);
-}
-
-static void *kvm_observe(void *arg)
-{
- long index = (long)arg;
-
-#define R(n) ((int)(regs.r##n)) // TODO: Adjust per arch?
- while (1) {
- if (cpu[index].state == OFFLINE)
- break;
-
- struct kvm_regs regs = { 0 };
- if (ioctl(cpu[index].vcpu, KVM_GET_REGS, &regs) < 0) {
- errln("Couldn't get KVM regs: %s", strerror(errno));
- break;
- }
-
- struct kvm_sregs sregs = { 0 };
- if (ioctl(cpu[index].vcpu, KVM_GET_SREGS, &sregs) < 0) {
- errln("Couldn't get KVM sregs: %s", strerror(errno));
- break;
- }
-
- logln("EAX: %08x ECX: %08x EDX: %08x EBX: %08x", R(ax), R(cx), R(dx), R(bx));
- logln("ESP: %08x EBP: %08x ESI: %08x EDI: %08x", R(ax), R(cx), R(dx), R(bx));
- logln("EFLAGS: %08x EIP: %08x", R(flags), R(ip));
-
- logln("ES.sel=%04x ES.base=%08x, ES.lim=%08x", sregs.es.selector, sregs.es.base,
- sregs.es.limit);
- logln("CS.sel=%04x CS.base=%08x, CS.lim=%08x", sregs.cs.selector, sregs.cs.base,
- sregs.cs.limit);
- logln("SS.sel=%04x SS.base=%08x, SS.lim=%08x", sregs.ss.selector, sregs.ss.base,
- sregs.ss.limit);
- logln("DS.sel=%04x DS.base=%08x, DS.lim=%08x", sregs.ds.selector, sregs.ds.base,
- sregs.ds.limit);
- logln("FS.sel=%04x FS.base=%08x, FS.lim=%08x", sregs.fs.selector, sregs.fs.base,
- sregs.fs.limit);
- logln("GS.sel=%04x GS.base=%08x, GS.lim=%08x", sregs.gs.selector, sregs.gs.base,
- sregs.gs.limit);
- logln("CR0: %08x CR2: %08x CR3: %08x CR4: %08x", sregs.cr0, sregs.cr2, sregs.cr3,
- sregs.cr4);
-
- logln("GDT.base=%08x GDT.limit=%08x", sregs.gdt.base, sregs.gdt.limit);
- logln("LDT.base=%08x LDT.limit=%08x", sregs.ldt.base, sregs.ldt.limit);
- logln("IDT.base=%08x IDT.limit=%08x", sregs.idt.base, sregs.idt.limit);
- logln("TR.base =%08x TR.limit =%08x", sregs.tr.base, sregs.tr.limit);
-
- struct timespec req = { 0 }, rem = { 0 };
- req.tv_sec = 1;
- int slept = nanosleep(&req, &rem);
- assert(slept >= 0);
- }
-
- logln("Done");
- pthread_exit(NULL);
-}
-
-static void *kvm_exec_cpu(void *arg)
-{
- pthread_attr_t attr = { 0 };
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
- int err;
-
- pthread_t observee = { 0 };
- err = pthread_create(&observee, &attr, kvm_loop, arg);
- if (err) {
- errln("Couldn't create observee thread: %d", err);
- pthread_exit(NULL);
- }
-
- pthread_t observer = { 0 };
- err = pthread_create(&observer, &attr, kvm_observe, arg);
- if (err) {
- errln("Couldn't create observer thread: %d", err);
- pthread_exit(NULL);
- }
-
- pthread_attr_destroy(&attr);
-
- logln("Done");
- pthread_exit(NULL);
-}
-
-void kvm_exec(void)
-{
- for (unsigned long i = 0; i < sizeof(cpu) / sizeof(cpu[0]); i++) {
- if (cpu[i].state == ONLINE) {
- logln("Booting CPU %d", i);
-
- pthread_attr_t attr = { 0 };
- pthread_attr_init(&attr);
- pthread_create(&cpu[i].thread, &attr, kvm_exec_cpu, (void *)i);
- pthread_attr_destroy(&attr);
- }
- }
-
- // Wait until all CPUs are finished
- for (unsigned long i = 0; i < sizeof(cpu) / sizeof(cpu[0]); i++)
- pthread_join(cpu[i].thread, NULL);
-
- kvm_destroy();
-}
-
-void kvm_destroy(void)
-{
- for (unsigned long i = 0; i < sizeof(cpu) / sizeof(cpu[0]); i++)
- if (cpu[i].vcpu)
- close(cpu[i].vcpu);
-
- if (vm)
- close(vm);
- if (kvm)
- close(kvm);
-
- logln("Done");
-}
diff --git a/src/linux.c b/src/linux.c
new file mode 100644
index 0000000..2e89049
--- /dev/null
+++ b/src/linux.c
@@ -0,0 +1,41 @@
+#include <stdint.h>
+#include <unistd.h>
+
+#include <cpu.h>
+#include <mem.h>
+#include <linux.h>
+#include <log.h>
+
+// TODO: Don't use unistd, emulate all syscalls for time-travel
+
+static void call_write(void)
+{
+ uint64_t fd = *(uint64_t *)cpu_get_reg(RDI);
+ char *buf = mem_phys(*(vaddr *)cpu_get_reg(RSI));
+ uint64_t count = *(uint64_t *)cpu_get_reg(RDX);
+ int ret = write(fd, buf, count);
+ cpu_set_reg(RAX, ret);
+}
+
+static void call_exit(void)
+{
+ int64_t code = *(int64_t *)cpu_get_reg(RDI);
+ logln("exiting with code %d", code);
+}
+
+int linux_call(void)
+{
+ uint64_t call = *(uint64_t *)cpu_get_reg(RAX);
+ switch (call) {
+ case 1:
+ call_write();
+ break;
+ case 60:
+ call_exit();
+ return ERR;
+ default:
+ errln("invalid syscall %d", call);
+ return ERR;
+ }
+ return OK;
+}
diff --git a/src/load.c b/src/load.c
new file mode 100644
index 0000000..ddcd54d
--- /dev/null
+++ b/src/load.c
@@ -0,0 +1,59 @@
+#include <elf.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <load.h>
+#include <log.h>
+
+vaddr load(const char *path)
+{
+ FILE *file = fopen(path, "rb");
+ if (!file) {
+ errln("can't read file %s.", path);
+ return 0;
+ }
+
+ Elf64_Ehdr header = { 0 };
+ fread(&header, sizeof(header), 1, file);
+ if (memcmp(header.e_ident, ELFMAG, SELFMAG)) {
+ errln("invalid ELF %s.", path);
+ return 0;
+ }
+
+ for (int i = 0; i < header.e_phnum; i++) {
+ int offset = header.e_phoff + header.e_phentsize * i;
+ if (fseek(file, offset, SEEK_SET)) {
+ errln("can't seek program to %x", offset);
+ return 0;
+ }
+
+ Elf64_Phdr program = { 0 };
+ if (fread(&program, sizeof(program), 1, file) != 1) {
+ errln("invalid program size %d", i);
+ return 0;
+ }
+
+ if (!program.p_vaddr || program.p_type != PT_LOAD)
+ continue;
+
+ void *addr = mem_alloc(program.p_memsz, program.p_vaddr);
+ if (!addr) {
+ errln("can't allocate %dB at %x", program.p_memsz,
+ program.p_vaddr);
+ return 0;
+ }
+ if (fseek(file, program.p_offset, SEEK_SET)) {
+ errln("can't seek program to %x", offset);
+ return 0;
+ }
+ if (fread(addr, program.p_filesz, 1, file) != 1) {
+ errln("invalid program size %d", i);
+ return 0;
+ }
+
+ logln("loaded program %d", i);
+ }
+
+ fclose(file);
+ return header.e_entry;
+}
diff --git a/src/main.c b/src/main.c
index 9ffe99a..b8bbe93 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,71 +1,19 @@
-#include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <kvm.h>
+#include <cpu.h>
+#include <mem.h>
#include <log.h>
-struct aalloc_info {
- void *actual_ptr;
-};
-
-static void *aalloc(int size, int align)
-{
- int adjusted = align - 1;
- void *actual = calloc(1, sizeof(void *) + size + adjusted);
- struct aalloc_info *ai =
- (void *)(((uintptr_t)((long)actual + sizeof(void *) + adjusted) & ~adjusted) -
- sizeof(void *));
- ai->actual_ptr = actual;
- return ((char *)ai) + sizeof(void *);
-}
-
-static void afree(void *ptr)
-{
- struct aalloc_info *a = (void *)((char *)ptr - sizeof(void *));
- free(a->actual_ptr);
-}
-
-static void load_bios(const char *path)
-{
- FILE *bios = fopen(path, "rb");
- if (!bios) {
- errln("Couldn't open '%s': %s", path, strerror(errno));
- exit(1);
- }
- fseek(bios, 0, SEEK_END);
- int size = ftell(bios);
- fseek(bios, 0, SEEK_SET);
-
- void *data = aalloc(size, 4096);
- if (fread(data, size, 1, bios) != 1) {
- errln("Couldn't read '%s': %s", path, strerror(errno));
- exit(1);
- }
-
- fclose(bios);
-
- int load = kvm_load(0, (long)data, size);
- if (load < 0) {
- errln("Couldn't load '%s'", path);
- exit(1);
- }
-
- afree(data);
-}
-
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
- int cpu = kvm_init();
- if (cpu < 0)
- exit(1);
- load_bios("build/test");
- kvm_exec();
+ if (argc != 2) {
+ logln("invalid arguments");
+ return 1;
+ }
+
+ cpu_exec(argv[1]);
+ mem_free_all();
return 0;
}
diff --git a/src/mem.c b/src/mem.c
new file mode 100644
index 0000000..1581bdc
--- /dev/null
+++ b/src/mem.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <mem.h>
+#include <log.h>
+
+// TODO: Better data structure
+struct memory {
+ size_t size;
+ vaddr virt; // for vm
+ void *physical; // actually alloced
+ struct memory *next;
+};
+
+static struct memory *memory = 0;
+static struct memory *head = 0;
+
+static uint64_t zero = 0;
+
+void *mem_alloc(size_t size, vaddr virt)
+{
+ if (head) {
+ memory->next = malloc(sizeof(*head));
+ memory = memory->next;
+ } else {
+ head = malloc(sizeof(*head));
+ memory = head;
+ }
+ memory->size = size;
+ memory->virt = virt;
+ memory->physical = calloc(1, size);
+ memory->next = 0;
+ return memory->physical;
+}
+
+void *mem_phys(vaddr virt)
+{
+ struct memory *iterator = head;
+ while (iterator) {
+ if (virt >= iterator->virt &&
+ virt < iterator->virt + iterator->size)
+ return iterator->physical + (virt - iterator->virt);
+ iterator = iterator->next;
+ }
+ errln("illegal memory access %x", virt);
+ return &zero;
+}
+
+void mem_free_all(void)
+{
+ struct memory *iterator = head;
+ while (iterator) {
+ free(iterator->physical);
+ struct memory *temp = iterator->next;
+ free(iterator);
+ iterator = temp;
+ }
+}
diff --git a/test.asm b/test.asm
deleted file mode 100644
index f115ca4..0000000
--- a/test.asm
+++ /dev/null
@@ -1,12 +0,0 @@
-xor dx, dx
-
-test:
-in ax, 0x60
-mov bx, 42
-
-mov cx, 1
-add dx, cx
-cmp dx, 255
-jne test
-
-hlt
diff --git a/tests/asm/yay.asm b/tests/asm/yay.asm
new file mode 100644
index 0000000..beffcb6
--- /dev/null
+++ b/tests/asm/yay.asm
@@ -0,0 +1,17 @@
+section .data
+
+msg: db "yay", 10
+len: equ $-msg
+
+section .text
+global main
+main:
+ mov rax, 1
+ mov rdi, 1
+ mov esi, msg
+ mov rdx, len
+ syscall
+
+ mov rax, 60
+ mov rdi, 0
+ syscall