diff options
author | Marvin Borner | 2023-01-28 22:29:48 +0100 |
---|---|---|
committer | Marvin Borner | 2023-01-28 22:29:48 +0100 |
commit | de450bfb4354f716fb43fae69d90ed513068d10b (patch) | |
tree | b474219ae3baaeb8a075e6fbb5c57c3857faa588 | |
parent | 75bb42663294b388377178b7257c6f8c0f787032 (diff) |
Rewrite, bootstrap
Rewriting without KVM, huge goals, won't finish, just for fun.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 28 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | inc/cpu.h | 57 | ||||
-rw-r--r-- | inc/kvm.h | 9 | ||||
-rw-r--r-- | inc/linux.h | 6 | ||||
-rw-r--r-- | inc/load.h | 8 | ||||
-rw-r--r-- | inc/mem.h | 13 | ||||
-rw-r--r-- | makefile | 50 | ||||
-rw-r--r-- | readme.md | 5 | ||||
-rw-r--r-- | src/cpu.c | 206 | ||||
-rw-r--r-- | src/kvm.c | 344 | ||||
-rw-r--r-- | src/linux.c | 41 | ||||
-rw-r--r-- | src/load.c | 59 | ||||
-rw-r--r-- | src/main.c | 70 | ||||
-rw-r--r-- | src/mem.c | 58 | ||||
-rw-r--r-- | test.asm | 12 | ||||
-rw-r--r-- | tests/asm/yay.asm | 17 |
18 files changed, 530 insertions, 457 deletions
@@ -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 ®s[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, ®ion) < 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, ®s) < 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, ®s) < 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, ®s) < 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; +} @@ -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 |