diff --git a/README.md b/README.md index dd40d82..0391468 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Emulator exit codes: - `0` - never happens - `1` - invalid opcode - `2` - illegal instruction argument (like funct3) +- `3` - memory error (for example writing to ROM) Compile: 1. Dependencies: `libelf` diff --git a/include/address_space.h b/include/address_space.h index 202211a..3f7df9f 100644 --- a/include/address_space.h +++ b/include/address_space.h @@ -2,11 +2,19 @@ #define ADDRESS_SPACE_H #include +#include struct AddressSpace_s { + // A pointer to a ROM array. The array can vary in length. uint8_t *rom; - uint32_t romSize; + // The size of ROM. + uint32_t romSize; // TODO look into making it const + // Is ROM read only + bool romLocked; + + // A pointer to a RAM array. The array can vary in length. uint8_t *ram; + // The size of RAM. uint32_t ramSize; }; diff --git a/programs/segfault.elf b/programs/segfault.elf new file mode 100755 index 0000000..1f8b6ce Binary files /dev/null and b/programs/segfault.elf differ diff --git a/programs/src/segfault.c b/programs/src/segfault.c new file mode 100644 index 0000000..6b4478f --- /dev/null +++ b/programs/src/segfault.c @@ -0,0 +1,15 @@ +/** + * This normally causes a segfault, but won't on the emulator because it requires a strict check. + * The emulator emulates bare metal, where there's no such checks. + */ + +int main() { + int *i = (int*)1; + *i = 10; + + for (int j = 0; j < 100; j++) { + *(i + j) = j; + } + + return i; +} \ No newline at end of file diff --git a/src/address_space.c b/src/address_space.c index 4b71446..f0646e4 100644 --- a/src/address_space.c +++ b/src/address_space.c @@ -3,20 +3,7 @@ #include #include -struct AddressSpace_s { - // A pointer to a ROM array. The array can vary in length. - uint8_t *rom; - // The size of ROM. - uint32_t romSize; // TODO look into making it const - - // A pointer to a RAM array. The array can vary in length. - uint8_t *ram; - // The size of RAM. - uint32_t ramSize; -}; - -typedef struct AddressSpace_s AddressSpace; - +#include "address_space.h" AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSize) { uint8_t *rom = calloc(romSize, 1); @@ -33,6 +20,7 @@ AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSiz addressSpace->rom = rom; addressSpace->romSize = romSize; + addressSpace->romLocked = false; addressSpace->ram = ram; addressSpace->ramSize = ramSize; @@ -71,6 +59,11 @@ int write_address_space(const AddressSpace *addressSpace, const uint32_t address uint32_t ramSize = addressSpace->ramSize; if (address < romSize) { + if (addressSpace->romLocked) { + fprintf(stderr, "Cannot write to ROM\n"); + return 2; + } + if (address + n >= romSize) { fprintf(stderr, "Writing %d bytes to %d will exceed ROM address space of %d\n", n, address, romSize); return 1; diff --git a/src/cpu.c b/src/cpu.c index 599f8f1..fbd0ea8 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -69,8 +69,7 @@ void print_registers(CPU *cpu) { for (int rown=0; rownd_buf; - uint32_t contentSize = data->d_size; - - if (contentSize > addressSpace->romSize) { - fprintf(stderr, "Code has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", contentSize, addressSpace->romSize); - return 1; + text = (uint8_t *)data->d_buf; + textSize = data->d_size; + } else if (strcmp(sectionName, ".rodata") == 0) { + Elf_Data *data = elf_getdata(scn, NULL); + + if (data == NULL || data->d_size == 0) { + fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1)); + continue; } - memcpy(addressSpace->rom, content, contentSize); - - printf("Loaded %d bytes or %d instructions from an ELF file\n", contentSize, contentSize / 4); + rodata = (uint8_t *)data->d_buf; + rodataSize = data->d_size; } else { printf("Unrecognized section: %s\n", sectionName); } @@ -93,6 +100,30 @@ static inline int process_elf(Elf *elf, CPU *cpu) { printf("I couldn't verify whether the ELF was compiled for the correct instruction set!\n"); } + if (textSize > 0) { + if (textSize > addressSpace->romSize) { + fprintf(stderr, "Code has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", textSize, addressSpace->romSize); + return 1; + } + + memcpy(addressSpace->rom, text, textSize); + } else { + fprintf(stderr, "There's no code\n"); + return 1; + } + + if (rodataSize > 0) { + uint32_t totalSize = rodataSize + textSize; + if (totalSize > addressSpace->romSize) { + fprintf(stderr, "Code %d + rodata %d = %d bytes, which doesn't fit in ROM of capacity %d bytes\n", textSize, rodataSize, totalSize, addressSpace->romSize); + return 1; + } + + memcpy(addressSpace->rom + textSize, rodata, rodataSize); + } + + printf("Loaded from ELF | Code: %d bytes (%d insts) | ROdata: %d bytes \n", textSize, textSize / 4, rodataSize); + cpu->programCounter = ehdr.e_entry; return 0; diff --git a/src/instruction_executor.c b/src/instruction_executor.c index 3b90e66..3be1b53 100644 --- a/src/instruction_executor.c +++ b/src/instruction_executor.c @@ -2,7 +2,7 @@ #include #include -#define VERBOSE_LOGGING 0 +#define VERBOSE_LOGGING 1 static void printBinary(uint32_t num) { if (num > 1) { @@ -40,8 +40,8 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside int32_t imm = ((int32_t)instruction >> 31) << 12; // imm[12] and sign extension imm |= ((instruction >> 7) & 0x1) << 11; // imm[11] - imm |= ((instruction >> 25) & 0x3f) << 5; // imm[10:5] - imm |= ((instruction >> 8) & 0xf) << 1; // imm[4:1] + imm |= ((instruction >> 25) & 0x3F) << 5; // imm[10:5] + imm |= ((instruction >> 8) & 0xF) << 1; // imm[4:1] // imm[0] is implicitly 0 bool branch = false; @@ -69,10 +69,10 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside } if (branch) { - printf(" Yes, branching"); + printf(" Yes, branching by 0x%X to 0x%X", imm, cpu->programCounter + imm); cpu->programCounter += imm - 4; // program counter is always incremented after executing instruction so -4 } else { - printf(" No, not branching"); + printf(" No, not branching by 0x%X to 0x%X", imm, cpu->programCounter + imm); } break; } @@ -82,15 +82,29 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside int32_t imm = (int32_t)instruction >> 20; switch (funct3) { - case 0b000: // ADDI - printf("ADDI - x%u 0x%X (%d) + 0x%X (%d) = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, imm, registers[rs1] + imm, rd); + case 0b000: { + // ADDI + printf("ADDI: x%u 0x%X (%d) + 0x%X (%d) = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, imm, registers[rs1] + imm, rd); registers[rd] = registers[rs1] + imm; break; - default: + } + case 0b001: { // SLLI + uint8_t shamt = imm & 0x1F; + imm = imm >> 5 & 0x3F; + if (imm != 0b0000000) { + fprintf(stderr, "Invalid SLLI imm[11:5], must be zero but is 0x%X", (imm >> 5 & 0x3F)); + return 2; + } + printf("SLLI: x%u 0x%X (%d) << %d = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, registers[rs1] << shamt, rd); + registers[rd] = registers[rs1] << shamt; + break; + } + default: { // TODO proper error handling fprintf(stderr, "Invalid OP-IMM funct3: 0x%X", funct3); return 2; break; + } } /* code */ break; @@ -119,11 +133,11 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside registers[rd] = cpu->programCounter + 4; cpu->programCounter = result - 4; // program counter is always incremented after executing instruction - printf("JALR - x%u %u + %u = %u", rs1, result - imm, imm, result); + printf("JALR - x%u %u + %u = 0x%X", rs1, result - imm, imm, result); break; } - case 0b0100011: { // STORE instruction (S type) + case 0b0100011: { // STORE instruction (S type), write errors are ignored uint8_t funct3 = instruction >> 12 & 0x7; uint8_t rs1 = instruction >> 15 & 0x1F; uint8_t rs2 = instruction >> 20 & 0x1F; @@ -152,6 +166,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside fprintf(stderr, "Invalid STORE funct3: 0x%X", funct3); return 2; } + break; } case 0b0000011: { // LOAD instruction (I type) @@ -218,7 +233,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside registers[rd] = cpu->programCounter; // program counter is always incremented after executing instruction cpu->programCounter += imm; - printf("JAL: Jumped to %u + %d = %u (inst %d)", registers[rd], imm, cpu->programCounter, cpu->programCounter / 4); + printf("JAL: Jumped to %u + %d = 0x%X (inst %d)", registers[rd], imm, cpu->programCounter, cpu->programCounter / 4); break; } case 0b0110011: { // OP for Integer Register-Register Operations (R type) diff --git a/src/main.c b/src/main.c index 2cb0be5..36c7605 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { return 1; } - AddressSpace *addressSpace = create_address_space(256, 256); + AddressSpace *addressSpace = create_address_space(512, 512); printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize); CPU cpu = create_cpu(addressSpace); @@ -31,6 +31,8 @@ int main(int argc, char *argv[]) { return 1; } + addressSpace->romLocked = true; + printf("\n----- Start of program (0x%X) -----", cpu.programCounter); @@ -47,6 +49,7 @@ int main(int argc, char *argv[]) { printf(" Program exit code: \033[1m%d\033[0m (x10)\n", cpu.registers[10]); printf(" Cycles: \033[1m%u\033[0m\n\n", cycles); + cpu.registers[0] = 0; // if x0 is changed in the last instruction (it usually is) it will be displayed what if was set to print_registers(&cpu); return 0;