diff --git a/.gitignore b/.gitignore index 608b1d4..1d597b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ -build/ \ No newline at end of file +build/ +programs/tests/ \ No newline at end of file diff --git a/README.md b/README.md index dd40d82..0795c4a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -RISC-V (rv32i) emulator in C \ +RISC-V (rv32mi) emulator in C \ This is just for me to understand how all this works, and to learn something new. \ So don't use it. diff --git a/src/elf_program_loader.c b/src/elf_program_loader.c index d590833..378e264 100644 --- a/src/elf_program_loader.c +++ b/src/elf_program_loader.c @@ -40,11 +40,16 @@ static inline int process_elf(Elf *elf, CPU *cpu) { return 1; } + /*uint32_t pretextSize = 0; + uint8_t *pretext; + uint32_t textSize = 0; uint8_t *text; uint32_t rodataSize = 0; - uint8_t *rodata; + uint8_t *rodata;*/ + + uint32_t written = 0; while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { @@ -54,9 +59,9 @@ static inline int process_elf(Elf *elf, CPU *cpu) { char *sectionName = elf_strptr(elf, shdrstrndx, shdr.sh_name); - Elf_Data *data = elf_getdata(scn, NULL); + Elf_Data *sectionData = elf_getdata(scn, NULL); - if (data == NULL || data->d_size == 0) { + if (sectionData == NULL || sectionData->d_size == 0) { fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1)); continue; } @@ -64,19 +69,49 @@ static inline int process_elf(Elf *elf, CPU *cpu) { if (shdr.sh_type == SHT_RISCV_ATTRIBUTES) { // TODO this is highly fragile // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc - char *content = (char *)data->d_buf; + char *content = (char *)sectionData->d_buf; content += 19; // first 17 chars are subsection headers, and I have to +2 to make strcmp match if (strcmp(content, "rv32i2p1") == 0) { warnedArch = 1; } + } else if (strcmp(sectionName, ".text.init") == 0) { + uint8_t *data = (uint8_t *)sectionData->d_buf; + uint32_t dataLength = sectionData->d_size; + + if (written + dataLength > addressSpace->romSize) { + fprintf(stderr, "Section %s has %d bytes, already written %d bytes, and doesn't fit in ROM of capacity %d bytes\n", sectionName, dataLength, written, addressSpace->romSize); + return 1; + } + + printf("Copying %d bytes from %s\n", dataLength, sectionName); + memcpy(addressSpace->rom + written, data, dataLength); + written += dataLength; } else if (strcmp(sectionName, ".text") == 0) { - text = (uint8_t *)data->d_buf; - textSize = data->d_size; + uint8_t *data = (uint8_t *)sectionData->d_buf; + uint32_t dataLength = sectionData->d_size; + + if (written + dataLength > addressSpace->romSize) { + fprintf(stderr, "Section %s has %d bytes, already written %d bytes, and doesn't fit in ROM of capacity %d bytes\n", sectionName, dataLength, written, addressSpace->romSize); + return 1; + } + + printf("Copying %d bytes from %s\n", dataLength, sectionName); + memcpy(addressSpace->rom + written, data, dataLength); + written += dataLength; } else if (strcmp(sectionName, ".rodata") == 0) { - rodata = (uint8_t *)data->d_buf; - rodataSize = data->d_size; + uint8_t *data = (uint8_t *)sectionData->d_buf; + uint32_t dataLength = sectionData->d_size; + + if (written + dataLength > addressSpace->romSize) { + fprintf(stderr, "Section %s has %d bytes, already written %d bytes, and doesn't fit in ROM of capacity %d bytes\n", sectionName, dataLength, written, addressSpace->romSize); + return 1; + } + + printf("Copying %d bytes from %s\n", dataLength, sectionName); + memcpy(addressSpace->rom + written, data, dataLength); + written += dataLength; } else { - printf("Unrecognized section: %s (%ub)\n", sectionName, (uint32_t)data->d_size); + printf("Unrecognized section: %s (%ub)\n", sectionName, (uint32_t)sectionData->d_size); } } @@ -84,6 +119,15 @@ 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 (pretextSize > 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); + } + 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); @@ -91,7 +135,7 @@ static inline int process_elf(Elf *elf, CPU *cpu) { } memcpy(addressSpace->rom, text, textSize); - } else { + } else if (pretextSize == 0) { fprintf(stderr, "There's no code\n"); return 1; } @@ -104,11 +148,15 @@ static inline int process_elf(Elf *elf, CPU *cpu) { } memcpy(addressSpace->rom + textSize, rodata, rodataSize); - } + }*/ - printf("Loaded from ELF | Code: %d bytes (%d insts) | ROdata: %d bytes \n", textSize, textSize / 4, rodataSize); + //printf("Loaded from ELF | Code: %d bytes (%d insts) | ROdata: %d bytes \n", textSize, textSize / 4, rodataSize); + printf("Loaded %d bytes from ELF\n", written); cpu->programCounter = ehdr.e_entry; + if (cpu->programCounter == 0x80000000) { // TODO not + cpu->programCounter = 0x0; + } return 0; } diff --git a/src/instruction_executor.c b/src/instruction_executor.c index 47fab10..493a6b4 100644 --- a/src/instruction_executor.c +++ b/src/instruction_executor.c @@ -277,6 +277,29 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside break; } + case 0b1110011: {// SYSTEM (I type) instructions are used to access system functionality that might require privileged access, here only ECALL and EBREAK + uint8_t funct3 = instruction >> 12 & 0x7; + uint32_t rs1 = instruction >> 15 & 0x1F; + uint32_t func12 = instruction >> 20 & 0xFFF; + + if (funct3 != 0 || rd != 0 || rs1 != 0) { + fprintf(stderr, "For SYSTEM instructions, funct3, rd and rs1 must be 0, but are 0x%X, 0x%X and 0x%X. Zicsr is not supported.", funct3, rd, rs1); + return 2; + } + + if (func12 == 0) { // ECALL + printf("--- ECALL ---"); + // TODO do something + } else if (func12 == 1) { // EBREAK + printf("--- EBREAK---"); + // TODO do something + } else { + fprintf(stderr, "Invalid SYSTEM func12: 0x%X", func12); + return 2; + } + + break; + } default: { // TODO illegal instruction, proper error handling fprintf(stderr, "Unrecognized opcode: 0x%X", opcode); diff --git a/src/main.c b/src/main.c index bf8aa87..5981457 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(512, 512); + AddressSpace *addressSpace = create_address_space(2048, 512); printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize); CPU cpu = create_cpu(addressSpace); @@ -23,7 +23,6 @@ int main(int argc, char *argv[]) { int lres = load_to_rom(argv[1], addressSpace); if (lres == -1) { - printf("Loading a binary file is deprecated. Please use an ELF.\n"); lres = load_elf_to_cpu_and_rom(argv[1], &cpu); } diff --git a/src/program_loader.c b/src/program_loader.c index 14a282e..3377151 100644 --- a/src/program_loader.c +++ b/src/program_loader.c @@ -26,6 +26,8 @@ int load_to_rom(const char filename[], AddressSpace *addressSpace) { return -1; } + printf("Loading a binary file is deprecated. Please use an ELF.\n"); + // size_t is not used because a program can't be larger than the 32bit address space int romSize = addressSpace->romSize; int bytesRead = fread(addressSpace->rom, 1, romSize, file);