From 40b4d52e905227849785f6520d0381f19657820a Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sun, 12 May 2024 11:24:16 +0200 Subject: Reorganize all this shit --- emulator/main.c | 817 +------------------------------------------------------- 1 file changed, 8 insertions(+), 809 deletions(-) (limited to 'emulator/main.c') diff --git a/emulator/main.c b/emulator/main.c index ed875a3..a00a717 100644 --- a/emulator/main.c +++ b/emulator/main.c @@ -1,812 +1,12 @@ #include #include #include -#include -#include -#include -#define ET_NONE 0x00 -#define ET_REL 0x01 -#define ET_EXEC 0x02 -#define ET_DYN 0x03 -#define ET_CORE 0x04 - -struct Elf32Header -{ - uint8_t ident[16]; - uint16_t type; - uint16_t machine; - uint32_t version; - uint32_t entry; - uint32_t phoff; - uint32_t shoff; - uint32_t flags; - uint16_t ehsize; - uint16_t phentsize; - uint16_t phnum; - uint16_t shentsize; - uint16_t shnum; - uint16_t shstrndx; -}; - -#define PT_NULL 0x00000000 -#define PT_LOAD 0x00000001 -#define PT_DYNAMIC 0x00000002 -#define PT_INTERP 0x00000003 -#define PT_NOTE 0x00000004 -#define PT_SHLIB 0x00000005 -#define PT_PHDR 0x00000006 -#define PT_TLS 0x00000007 - -#define PF_X 0x1 -#define PF_W 0x2 -#define PF_R 0x4 - -struct Elf32ProgramHeader -{ - uint32_t type; - uint32_t offset; - uint32_t vaddr; - uint32_t paddr; - uint32_t filesz; - uint32_t memsz; - uint32_t flags; - uint32_t align; -}; - -bool load_elf(const char* file, char* mem, uint32_t mem_size, uint32_t* start_address) -{ - FILE* fp = fopen(file, "rb"); - if (fp == NULL) - { - printf("Couldn't open input file\n"); - return false; - } - - struct Elf32Header header; - size_t read = fread(&header, sizeof(struct Elf32Header), 1, fp); - if (read != 1) - { - printf("ELF header too small\n"); - return false; - } - - static uint8_t kValidIdent[7] = { - 0x7f, 0x45, 0x4c, 0x46, - 1, 1, 1 - }; - if (memcmp(kValidIdent, header.ident, 7) != 0) - { - printf("Invalid ELF header\n"); - return false; - } - - if (header.type != ET_EXEC) - { - printf("Can only link EXEC ELF files\n"); - return false; - } - - memset(mem, 0, mem_size); - - assert(header.phnum == 0 || header.phentsize == sizeof(struct Elf32ProgramHeader)); - for (uint32_t i = 0; i < header.phnum; ++i) - { - struct Elf32ProgramHeader pheader; - - fseek(fp, header.phoff + i * header.phentsize, SEEK_SET); - read = fread(&pheader, sizeof(struct Elf32ProgramHeader), 1, fp); - if (read != 1) - { - printf("Error reading program header at index %u\n", i); - return false; - } - - if (pheader.type == PT_LOAD) - { - if (pheader.paddr + pheader.memsz > mem_size) - { - printf("Memory not large enough for ELF file\n"); - return false; - } - - fseek(fp, pheader.offset, SEEK_SET); - read = fread(mem + pheader.paddr, pheader.filesz, 1, fp); - if (read != 1) - { - printf("Error when copying ELF segment\n"); - return false; - } - } - } - - if (header.entry > mem_size) - { - printf("Entry address out of bounds\n"); - return false; - } - - *start_address = header.entry; - fclose(fp); - - return true; -} - -inline uint32_t sign_extend(uint32_t word, uint32_t size) -{ - const uint32_t mask = 1U << (size - 1); - return (word ^ mask) - mask; -} - -struct Instruction -{ - uint8_t opcode; - uint8_t rs1; - uint8_t rs2; - uint8_t rd; - uint8_t funct3; - uint8_t funct7; - uint32_t imm; -}; - -struct Instruction decode_r_type(uint32_t word) -{ - struct Instruction instruction = {0}; - instruction.opcode = word & 0x7F; - instruction.rd = (word >> 7) & 0x1F; - instruction.funct3 = (word >> 12) & 0x07; - instruction.rs1 = (word >> 15) & 0x1F; - instruction.rs2 = (word >> 20) & 0x1F; - instruction.funct7 = word >> 25; - return instruction; -}; - -struct Instruction decode_i_type(uint32_t word) -{ - struct Instruction instruction = {0}; - instruction.opcode = word & 0x7F; - instruction.rd = (word >> 7) & 0x1F; - instruction.funct3 = (word >> 12) & 0x07; - instruction.rs1 = (word >> 15) & 0x1F; - instruction.imm = sign_extend(word >> 20, 12); - return instruction; -}; - -struct Instruction decode_s_type(uint32_t word) -{ - struct Instruction instruction = {0}; - instruction.opcode = word & 0x7F; - instruction.funct3 = (word >> 12) & 0x07; - instruction.rs1 = (word >> 15) & 0x1F; - instruction.rs2 = (word >> 20) & 0x1F; - instruction.imm = sign_extend(((word >> 7) & 0x1F) | (word >> 25), 12); - return instruction; -}; - -struct Instruction decode_b_type(uint32_t word) -{ - struct Instruction instruction = decode_s_type(word); - instruction.imm = ((instruction.imm << 11) & 0x800) | (instruction.imm & 0xfffff7ff); - return instruction; -}; - -struct Instruction decode_u_type(uint32_t word) -{ - struct Instruction instruction = {0}; - instruction.opcode = word & 0x7F; - instruction.rd = (word >> 7) & 0x1F; - instruction.imm = word & 0xFFFFF000; - return instruction; -}; - -struct Instruction decode_j_type(uint32_t word) -{ - struct Instruction instruction = {0}; - instruction.opcode = word & 0x7F; - instruction.rd = (word >> 7) & 0x1F; - instruction.imm = sign_extend( - ((word & 0x80000000) >> 11) | - ((word & 0x000FF000) >> 0) | - ((word & 0x00100000) >> 9) | - ((word & 0x7FE00000) >> 20), 21); - return instruction; -} - -struct Hart -{ - uint32_t pc; - uint32_t regs[32]; - char* mem; - uint32_t mem_size; -}; - -void execute_op_imm(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_i_type(instruction); - if (inst.rd == 0) return; - - switch (inst.funct3) - { - case 0: // ADDI - hart->regs[inst.rd] = hart->regs[inst.rs1] + inst.imm; - break; - case 1: // SLLI - hart->regs[inst.rd] = hart->regs[inst.rs1] << (inst.imm & 0x1F); - break; - case 2: // SLTI - hart->regs[inst.rd] = (int32_t)hart->regs[inst.rs1] < (int32_t)inst.imm ? 1 : 0; - break; - case 3: // SLTIU - hart->regs[inst.rd] = hart->regs[inst.rs1] < inst.imm ? 1 : 0; - break; - case 4: // XORI - hart->regs[inst.rd] = hart->regs[inst.rs1] ^ inst.imm; - break; - case 5: // SRLI, SRAI - { - const uint32_t shamt = inst.imm & 0x1F; - uint32_t res = hart->regs[inst.rs1] >> shamt; - if ((inst.imm & 0x400) && shamt > 0) { res = sign_extend(res, 32 - shamt); } - hart->regs[inst.rd] = res; - break; - } - case 6: // ORI - hart->regs[inst.rd] = hart->regs[inst.rs1] | inst.imm; - break; - case 7: // ANDI - hart->regs[inst.rd] = hart->regs[inst.rs1] & inst.imm; - break; - default: - assert(!"Unhandled OP-IMM"); - } -} - -void execute_op(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_r_type(instruction); - if (inst.rd == 0) return; - - switch (inst.funct3) - { - case 0: // ADD, SUB - if (instruction & 0x40000000) - { - hart->regs[inst.rd] = hart->regs[inst.rs1] - hart->regs[inst.rs2]; - } - else - { - hart->regs[inst.rd] = hart->regs[inst.rs1] + hart->regs[inst.rs2]; - } - break; - case 1: // SLL - hart->regs[inst.rd] = hart->regs[inst.rs1] << (hart->regs[inst.rs2] & 0x1F); - break; - case 2: // SLT - hart->regs[inst.rd] = (int32_t)hart->regs[inst.rs1] < (int32_t)hart->regs[inst.rs2] ? 1 : 0; - break; - case 3: // SLTU - hart->regs[inst.rd] = hart->regs[inst.rs1] < hart->regs[inst.rs2] ? 1 : 0; - break; - case 4: // XOR - hart->regs[inst.rd] = hart->regs[inst.rs1] ^ hart->regs[inst.rs2]; - break; - case 5: // SRL, SRA - { - const uint32_t shamt = hart->regs[inst.rs2] & 0x1F; - uint32_t res = hart->regs[inst.rs1] >> shamt; - if ((instruction & 0x40000000) && shamt > 0) { res = sign_extend(res, 32 - shamt); } - hart->regs[inst.rd] = res; - break; - } - case 6: // OR - hart->regs[inst.rd] = hart->regs[inst.rs1] | hart->regs[inst.rs2]; - break; - case 7: // AND - hart->regs[inst.rd] = hart->regs[inst.rs1] & hart->regs[inst.rs2]; - break; - default: - assert(!"Unhandled OP-IMM"); - } -} - -void execute_branch(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_b_type(instruction); - const uint32_t r1 = hart->regs[inst.rs1]; - const uint32_t r2 = hart->regs[inst.rs2]; - bool take_branch = false; - - switch (inst.funct3) - { - case 0: take_branch = (r1 == r2); break; - case 1: take_branch = (r1 != r2); break; - case 4: take_branch = (r1 < r2); break; - case 5: take_branch = (r1 >= r2); break; - case 6: take_branch = ((int32_t)r1 < (int32_t)r2); break; - case 7: take_branch = ((int32_t)r1 >= (int32_t)r2); break; - } - - if (take_branch) - { - hart->pc += inst.imm; - } - else - { - hart->pc += 4; - } -} - -inline uint32_t load_size(struct Hart* hart, uint32_t address, uint32_t size) -{ - if ((address & 0x80000000) == 0) - { - assert(address + size < hart->mem_size); - uint32_t value = 0; - memcpy(&value, hart->mem + address, size); - return value; - } - - return 0; -} - -uint32_t load_byte(struct Hart* hart, uint32_t address) -{ - return load_size(hart, address, 1); -} - -uint32_t load_half(struct Hart* hart, uint32_t address) -{ - return load_size(hart, address, 2); -} - -uint32_t load_word(struct Hart* hart, uint32_t address) -{ - return load_size(hart, address, 4); -} - -inline void store_size(struct Hart* hart, uint32_t address, uint32_t value, uint32_t size) -{ - if ((address & 0x80000000) == 0) - { - assert(address + size < hart->mem_size); - memcpy(hart->mem + address, &value, size); - } - else if (address == 0x80000000) - { - fwrite(&value, 1, size, stdout); - } -} - -void store_byte(struct Hart* hart, uint32_t address, uint8_t value) -{ - store_size(hart, address, value, 1); -} - -void store_half(struct Hart* hart, uint32_t address, uint16_t value) -{ - store_size(hart, address, value, 2); -} - -void store_word(struct Hart* hart, uint32_t address, uint32_t value) -{ - store_size(hart, address, value, 4); -} - -void execute_op_load(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_i_type(instruction); - const uint32_t address = hart->regs[inst.rs1] + inst.imm; - - uint32_t value = 0; - - switch (inst.funct3 & 0x03) - { - case 0: - value = load_byte(hart, address); - if ((inst.funct3 & 0x40) == 0) - { - value = sign_extend(value, 8); - } - break; - case 1: - value = load_half(hart, address); - if ((inst.funct3 & 0x40) == 0) - { - value = sign_extend(value, 16); - } - break; - case 2: - value = load_byte(hart, address); - break; - default: - assert(!"Unhandled load size"); - break; - } - - if (inst.rd != 0) - { - hart->regs[inst.rd] = value; - } -} - -void execute_op_store(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_s_type(instruction); - const uint32_t address = hart->regs[inst.rs1] + inst.imm; - const uint32_t value = hart->regs[inst.rs2]; - - switch (inst.funct3 & 0x03) - { - case 0: store_byte(hart, address, value & 0xFF); break; - case 1: store_half(hart, address, value & 0xFFFF); break; - case 2: store_word(hart, address, value); break; - default: - assert(!"Unhandled store size"); - break; - } -} - -void execute_misc_mem(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_i_type(instruction); - - if (inst.funct3 == 0) - { - // FENCE - } - else - { - assert(!"Unhandled MISC-MEM instruction"); - } -} - -void execute_system(struct Hart* hart, uint32_t instruction) -{ - const struct Instruction inst = decode_i_type(instruction); - - if (inst.funct3 == 0 && inst.rs1 == 0 && inst.rd == 0) - { - if (inst.imm == 0) - { - // ECALL - } - else if (inst.imm == 1) - { - // EBREAK - } - else - { - assert(!"Unhandled SYSTEM/PRIV instruction"); - } - } - else - { - assert(!"Unhandled SYSTEM instruction"); - } -} - -void execute(struct Hart* hart, uint32_t instruction) -{ - switch (instruction & 0x7f) - { - case 0x03: - execute_op_load(hart, instruction); - hart->pc += 4; - break; - case 0x0F: - execute_misc_mem(hart, instruction); - hart->pc += 4; - break; - case 0x13: - execute_op_imm(hart, instruction); - hart->pc += 4; - break; - case 0x17: // AUIPC - { - struct Instruction inst = decode_u_type(instruction); - if (inst.rd != 0) - { - hart->regs[inst.rd] = inst.imm + hart->pc; - } - hart->pc += 4; - break; - } - case 0x23: - execute_op_store(hart, instruction); - hart->pc += 4; - break; - case 0x33: - execute_op(hart, instruction); - hart->pc += 4; - break; - case 0x37: // LUI - { - struct Instruction inst = decode_u_type(instruction); - if (inst.rd != 0) - { - hart->regs[inst.rd] = inst.imm; - } - hart->pc += 4; - break; - } - case 0x63: - execute_branch(hart, instruction); - break; - case 0x67: // JALR - { - struct Instruction inst = decode_i_type(instruction); - assert(inst.funct3 == 0); - if (inst.rd != 0) - { - hart->regs[inst.rd] = hart->pc + 4; - } - hart->pc = (hart->regs[inst.rs1] + inst.imm) & 0xFFFFFFFE; - break; - } - case 0x6F: // JAL - { - struct Instruction inst = decode_j_type(instruction); - if (inst.rd != 0) - { - hart->regs[inst.rd] = hart->pc + 4; - } - hart->pc += inst.imm; - break; - } - case 0x73: - { - execute_system(hart, instruction); - hart->pc += 4; - break; - } - default: - assert(!"Unhandled opcode"); - } - - assert(hart->regs[0] == 0); -} - -void test_addi() -{ - struct Hart hart = {0}; - - execute(&hart, 0x00500093); // addi x1, x0, 5 - assert(hart.regs[1] == 5); - - execute(&hart, 0xffe00093); // addi, x1, x0, -2 - assert(hart.regs[1] == 0xfffffffe); -} - -void test_slti_sltiu() -{ - struct Hart hart = {0}; - - hart.regs[1] = 5; - - execute(&hart, 0x00f0b113); // sltiu x2, x1, 15 - assert(hart.regs[2] == 1); - - execute(&hart, 0x0050b113); // sltiu x2, x1, 15 - assert(hart.regs[2] == 0); - - execute(&hart, 0x0010b113); // sltiu x2, x1, 15 - assert(hart.regs[2] == 0); - - execute(&hart, 0x00f0a113); // slti x2, x1, 15 - assert(hart.regs[2] == 1); - - execute(&hart, 0x0050a113); // slti x2, x1, 15 - assert(hart.regs[2] == 0); - - execute(&hart, 0x0010a113); // slti x2, x1, 15 - assert(hart.regs[2] == 0); - - execute(&hart, 0xffb0a113); // slti x2, x1, -5 - assert(hart.regs[2] == 0); - - hart.regs[1] = (uint32_t)-20; - - execute(&hart, 0xffb0a113); // slti x2, x1, -5 - assert(hart.regs[2] == 1); -} - -void test_andi_ori_xori() -{ - struct Hart hart = {0}; - - hart.regs[1] = 6; - - execute(&hart, 0x00c0c113); // xori x2, x1, 12 - assert(hart.regs[2] == 10); - - execute(&hart, 0x00c0e113); // ori x2, x1, 12 - assert(hart.regs[2] == 14); - - execute(&hart, 0x00c0f113); // andi x2, x1, 12 - assert(hart.regs[2] == 4); -} - -void test_slli_srli_srai() -{ - struct Hart hart = {0}; - - hart.regs[1] = 6; - - execute(&hart, 0x00209113); // slli x2, x1, 2 - assert(hart.regs[2] == 24); - - execute(&hart, 0x0020d113); // srli x2, x1, 2 - assert(hart.regs[2] == 1); - - execute(&hart, 0x4020d113); // srai x2, x1, 2 - assert(hart.regs[2] == 1); - - hart.regs[1] = (uint32_t)-6; - - execute(&hart, 0x0020d113); // srli x2, x1, 2 - assert(hart.regs[2] == 0x3FFFFFFE); - - execute(&hart, 0x4020d113); // srai x2, x1, 2 - assert(hart.regs[2] == 0xFFFFFFFE); -} - -void test_lui_auipc() -{ - struct Hart hart = {0}; - - hart.pc = 0; - execute(&hart, 0x0007b0b7); // lui x1, 503808 - assert(hart.regs[1] == 503808); - - hart.pc = 0; - execute(&hart, 0x0007b097); // auipc x1, 503808 - assert(hart.regs[1] == 503808); - - hart.pc = 12; - execute(&hart, 0x0007b097); // auipc x1, 503808 - assert(hart.regs[1] == 503820); -} - -void test_op() -{ - struct Hart hart = {0}; - - hart.regs[1] = 3; - hart.regs[2] = 4; - hart.regs[4] = (uint32_t)-1; - - execute(&hart, 0x002081b3); // add, x3, x1, x2 - assert(hart.regs[3] == 7); - - execute(&hart, 0x402081b3); // sub x3, x1, x2 - assert(hart.regs[3] == 0xFFFFFFFF); - - execute(&hart, 0x0020a1b3); // slt x3, x1, x2 - assert(hart.regs[3] == 1); - - execute(&hart, 0x001121b3); // slt x3, x2, 1 - assert(hart.regs[3] == 0); - - execute(&hart, 0x001131b3); // sltu x3, x2, x1 - assert(hart.regs[3] == 0); - - execute(&hart, 0x0020b1b3); // sltu x3, x1, x2 - assert(hart.regs[3] == 1); - - execute(&hart, 0x0040a1b3); // slt x3, x1, x4 - assert(hart.regs[3] == 0); - - hart.regs[1] = 6; - hart.regs[2] = 12; - - execute(&hart, 0x0020e1b3); // or x3, x1, x2 - assert(hart.regs[3] == 14); - - execute(&hart, 0x0020c1b3); // xor x3, x1, x2 - assert(hart.regs[3] == 10); - - execute(&hart, 0x0020f1b3); // and x3, x1, x2 - assert(hart.regs[3] == 4); - - hart.regs[1] = 6; - hart.regs[2] = 2; - - execute(&hart, 0x002091b3); // sll x3, x1, x2 - assert(hart.regs[3] == 24); - - execute(&hart, 0x0020d1b3); // srl x3, x1, x2 - assert(hart.regs[3] == 1); - - execute(&hart, 0x4020d1b3); // sra x3, x1, x2 - assert(hart.regs[3] == 1); - - hart.regs[1] = (uint32_t)-6; - hart.regs[2] = 2; - - execute(&hart, 0x4020d1b3); // sra x3, x1, x2 - assert(hart.regs[3] == 0xFFFFFFFE); -} - -void test_jal() -{ - struct Hart hart = {0}; - - hart.pc = 12; - - execute(&hart, 0x12c000ef); // jal x1, 300 - assert(hart.regs[1] == 16); - assert(hart.pc == 312); - - execute(&hart, 0xed5ff0ef); // jal x1, -300 - assert(hart.regs[1] == 316); - assert(hart.pc == 12); -} - -void test_jalr() -{ - struct Hart hart = {0}; - - hart.pc = 12; - hart.regs[1] = 300; - - execute(&hart, 0x00a08167); // jalr x2, 10(x1) - assert(hart.regs[2] == 16); - assert(hart.pc == 310); - - execute(&hart, 0xff608167); // jalr x2, -10(x1) - assert(hart.regs[2] == 314); - assert(hart.pc == 290); -} - -void test_branch() -{ - struct Hart hart = {0}; - - hart.pc = 100; - hart.regs[1] = 2; - hart.regs[2] = 0xFFFFFFFC; - - execute(&hart, 0x00208c63); // beq x1, x2, 24 - assert(hart.pc == 104); - - hart.pc = 100; - execute(&hart, 0x00209c63); // bne x1, x2, 24 - assert(hart.pc == 124); - - hart.pc = 100; - execute(&hart, 0x0020cc63); // blt x1, x2, 24 - assert(hart.pc == 124); - - hart.pc = 100; - execute(&hart, 0x0020dc63); // bge x1, x2, 24 - assert(hart.pc == 104); - - hart.pc = 100; - execute(&hart, 0x0020ec63); // bltu x1, x2, 24 - assert(hart.pc == 104); - - hart.pc = 100; - execute(&hart, 0x0020fc63); // bgeu x1, x2, 24 - assert(hart.pc == 124); -} - -void test() -{ - test_addi(); - test_slti_sltiu(); - test_andi_ori_xori(); - test_slli_srli_srai(); - test_lui_auipc(); - test_op(); - test_jal(); - test_jalr(); - test_branch(); -} +#include "emulator/hart.h" +#include "emulator/elf.h" int main(int argc, char* argv[]) { - test(); - if (argc < 2) { printf("Usage: %s \n", argv[0]); @@ -824,15 +24,14 @@ int main(int argc, char* argv[]) } struct Hart hart = {0}; - hart.pc = start_address; hart.mem = mem; hart.mem_size = mem_size; - while (true) - { - uint32_t instruction = load_word(&hart, hart.pc); - execute(&hart, instruction); - } - + execute_from(&hart, start_address); + return EXIT_SUCCESS; } + +#include "emulator/hart.c" +#include "emulator/elf.c" + -- cgit