Improve vmem handling a bit

This commit is contained in:
Konstantin Nazarov 2024-12-18 23:05:34 +00:00
parent 2395b75e4d
commit dfc9b2824e
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
4 changed files with 120 additions and 125 deletions

View file

@ -1,5 +1,21 @@
#include "printf.h" #include "printf.h"
typedef unsigned long long uint64_t;
typedef unsigned long uint32_t;
#define MTIME_BASE 0x0200BFF8
uint64_t read_mtime_atomic() {
uint32_t upper1, lower, upper2;
do {
upper1 = *(uint32_t*)(MTIME_BASE + 4);
lower = *(uint32_t*)(MTIME_BASE);
upper2 = *(uint32_t*)(MTIME_BASE + 4);
} while (upper1 != upper2); // Repeat if upper changed during the process
return ((uint64_t)upper1 << 32) | lower;
}
int fact(int n) { int fact(int n) {
if (n == 0) if (n == 0)
return 1; return 1;
@ -11,6 +27,7 @@ int main() {
int n = 8; int n = 8;
int res = fact(8); int res = fact(8);
printf("sizeof(long) = %d\n", sizeof(long long));
printf("%d! = %d\n", n, res); printf("%d! = %d\n", n, res);
return 0; return 0;

View file

@ -228,9 +228,11 @@ void GDBStub::handle_packet(const std::string &packet) {
std::stoul(packet.substr(3, packet.find(',', 3)), nullptr, 16); std::stoul(packet.substr(3, packet.find(',', 3)), nullptr, 16);
if (breakpoints.count(addr) == 0) { if (breakpoints.count(addr) == 0) {
uint32_t original_instr = vm.read_memory_word(addr); uint32_t original_instr;
vm.read_mem((uint8_t *)&original_instr, addr, 4);
breakpoints[addr] = original_instr; breakpoints[addr] = original_instr;
vm.write_memory_word(addr, 0x00100073); // 0x00100073 is EBREAK uint32_t debug_instr = 0x00100073; // 0x00100073 is EBREAK
vm.write_mem((uint8_t *)&debug_instr, addr, 4);
} }
} }
send_packet("OK"); send_packet("OK");
@ -244,7 +246,7 @@ void GDBStub::handle_packet(const std::string &packet) {
if (breakpoints.count(addr) > 0) { if (breakpoints.count(addr) > 0) {
// Restore the original instruction // Restore the original instruction
vm.write_memory_word(addr, breakpoints[addr]); vm.write_mem((uint8_t *)&breakpoints[addr], addr, 4);
breakpoints.erase(addr); breakpoints.erase(addr);
} }
} }

View file

@ -12,26 +12,8 @@ inline int32_t sign_extend(int32_t value, int bits) {
return (value ^ mask) - mask; return (value ^ mask) - mask;
} }
uint8_t UART::read_register(uint32_t address) {
switch (address) {
case UART_LSR:
// Always ready to transmit
return LSR_TRANSMITTER_EMPTY;
default:
return 0;
}
}
void UART::write_register(uint32_t address, uint8_t value) {
switch (address) {
case UART_THR:
std::cout.put(static_cast<char>(value));
break;
}
}
bool UART::is_transmitter_ready() { bool UART::is_transmitter_ready() {
return read_register(UART_LSR) & LSR_TRANSMITTER_EMPTY; return registers[UART_LSR] & LSR_TRANSMITTER_EMPTY;
} }
VM::VM(const std::vector<uint8_t>& memory, const std::string& file_path) VM::VM(const std::vector<uint8_t>& memory, const std::string& file_path)
@ -45,96 +27,84 @@ void VM::setreg(int regnum, uint32_t value) {
registers[regnum] = value; registers[regnum] = value;
} }
void UART::read_mem(uint8_t* dst, size_t addr, size_t size) {
if (addr < UART_ADDR || addr + size > UART_ADDR + 8) {
throw std::runtime_error("Memory access out of bounds");
}
addr -= UART_ADDR;
for (size_t i = 0; i < size; i++) {
switch (addr + i) {
case UART_LSR:
// Always ready to transmit
dst[i] = LSR_TRANSMITTER_EMPTY;
default:
dst[i] = 0;
}
}
}
void UART::write_mem(uint8_t* src, size_t addr, size_t size) {
if (addr < UART_ADDR || addr + size > UART_ADDR + 8) {
throw std::runtime_error("Memory access out of bounds");
}
addr -= UART_ADDR;
for (size_t i = 0; i < size; i++) {
switch (addr + i) {
case UART_THR:
std::cout.put(static_cast<char>(*src));
break;
}
}
}
void VM::read_mem(uint8_t* dst, size_t addr, size_t size) {
if (addr >= PROGRAM_ADDR) {
addr -= PROGRAM_ADDR;
if (addr + size > memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
std::memcpy(dst, &memory_[addr], size);
}
}
void VM::write_mem(uint8_t* src, size_t addr, size_t size) {
if (addr >= PROGRAM_ADDR) {
addr -= PROGRAM_ADDR;
if (addr + size > memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
std::memcpy(&memory_[addr], src, size);
return;
}
if (is_mmap(addr, size)) {
if (addr >= UART_ADDR && addr < UART_ADDR + 8) {
uart.write_mem(src, addr, size);
}
return;
}
}
std::vector<uint8_t> VM::read_memory(size_t start, size_t size) { std::vector<uint8_t> VM::read_memory(size_t start, size_t size) {
if (start >= PROGRAM_ADDR) { std::vector<uint8_t> res(size, 0);
start -= PROGRAM_ADDR;
if (start + size > memory_.size()) {
return std::vector<uint8_t>(size, 0);
}
return std::vector<uint8_t>(memory_.begin() + start,
memory_.begin() + start + size);
} else {
return std::vector<uint8_t>(size, 0);
}
}
uint32_t VM::read_memory_word(size_t pos) { size_t i = start;
if (pos >= PROGRAM_ADDR) { size_t end = start + size;
pos -= PROGRAM_ADDR;
if (pos + 3 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
return *(uint32_t*)&memory_[pos];
} else {
throw std::runtime_error("Memory access out of bounds");
}
}
uint16_t VM::read_memory_half_word(size_t pos) { while (i < end) {
if (pos >= PROGRAM_ADDR) { if (i % 4 == 0 && end - i >= 4) {
pos -= PROGRAM_ADDR; read_mem(&res[i - start], i, 4);
if (pos + 1 >= memory_.size()) { i += 4;
throw std::runtime_error("Memory access out of bounds"); } else if (i % 2 == 0 && end - i >= 2) {
} read_mem(&res[i - start], i, 2);
i += 2;
return *(uint16_t*)&memory_[pos]; } else {
} else { read_mem(&res[i - start], i, 1);
throw std::runtime_error("Memory access out of bounds"); i += 1;
}
}
uint8_t VM::read_memory_byte(size_t pos) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
return memory_[pos];
} else {
throw std::runtime_error("Memory access out of bounds");
}
}
void VM::write_memory_word(size_t pos, uint32_t value) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 1 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
*(uint32_t*)&memory_[pos] = value;
} else {
throw std::runtime_error("Memory access out of bounds");
}
}
void VM::write_memory_half_word(size_t pos, uint16_t value) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 3 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
*(uint16_t*)&memory_[pos] = value;
} else {
throw std::runtime_error("Memory access out of bounds");
}
}
void VM::write_memory_byte(size_t pos, uint8_t value) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds");
}
memory_[pos] = value;
} else {
if (is_mmap(pos, 1)) {
if (pos >= UART_ADDR && pos < UART_ADDR + 8) {
uart.write_register(pos - UART_ADDR, value);
}
return;
} }
} }
return res;
} }
bool VM::is_mmap(size_t pos, size_t size) { bool VM::is_mmap(size_t pos, size_t size) {
@ -353,19 +323,29 @@ void VM::step() {
imm = sign_extend(instr >> 20, 12); // Extract 12-bit immediate imm = sign_extend(instr >> 20, 12); // Extract 12-bit immediate
if (funct3 == 0x00) { // LB if (funct3 == 0x00) { // LB
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
setreg(rd, sign_extend(read_memory_byte(addr), 8)); uint8_t val;
read_mem(&val, addr, 1);
setreg(rd, sign_extend(val, 8));
} else if (funct3 == 0x01) { // LH } else if (funct3 == 0x01) { // LH
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
setreg(rd, sign_extend(read_memory_half_word(addr), 16)); uint16_t val;
read_mem((uint8_t*)&val, addr, 2);
setreg(rd, sign_extend(val, 16));
} else if (funct3 == 0x2) { // LW } else if (funct3 == 0x2) { // LW
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
setreg(rd, read_memory_word(addr)); uint32_t val;
read_mem((uint8_t*)&val, addr, 4);
setreg(rd, val);
} else if (funct3 == 0x4) { // LBU } else if (funct3 == 0x4) { // LBU
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
setreg(rd, read_memory_byte(addr)); uint8_t val;
read_mem(&val, addr, 1);
setreg(rd, val);
} else if (funct3 == 0x5) { // LHU } else if (funct3 == 0x5) { // LHU
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
setreg(rd, read_memory_half_word(addr)); uint16_t val;
read_mem((uint8_t*)&val, addr, 2);
setreg(rd, val);
} else { } else {
throw std::runtime_error("Unknown load instruction"); throw std::runtime_error("Unknown load instruction");
} }
@ -376,13 +356,13 @@ void VM::step() {
imm = sign_extend(imm, 12); // Sign-extend 12-bit immediate imm = sign_extend(imm, 12); // Sign-extend 12-bit immediate
if (funct3 == 0x0) { // SB if (funct3 == 0x0) { // SB
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
write_memory_byte(addr, registers[rs2]); write_mem((uint8_t*)&registers[rs2], addr, 1);
} else if (funct3 == 0x1) { // SH } else if (funct3 == 0x1) { // SH
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
write_memory_half_word(addr, registers[rs2]); write_mem((uint8_t*)&registers[rs2], addr, 2);
} else if (funct3 == 0x2) { // SW } else if (funct3 == 0x2) { // SW
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
write_memory_word(addr, registers[rs2]); write_mem((uint8_t*)&registers[rs2], addr, 4);
} else { } else {
throw std::runtime_error("Unknown store instruction"); throw std::runtime_error("Unknown store instruction");
} }

View file

@ -2,6 +2,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
@ -13,11 +14,11 @@ const uint32_t PROGRAM_ADDR = 0x80000000;
class UART { class UART {
public: public:
uint8_t read_register(uint32_t address);
void write_register(uint32_t address, uint8_t value);
bool is_transmitter_ready(); bool is_transmitter_ready();
void write_mem(uint8_t *src, size_t addr, size_t size);
void read_mem(uint8_t *dst, size_t addr, size_t size);
private: private:
enum Registers { enum Registers {
UART_RBR = 0x00, // Receiver Buffer Register UART_RBR = 0x00, // Receiver Buffer Register
@ -40,13 +41,8 @@ class VM {
std::vector<uint8_t> read_memory(size_t start, size_t size); std::vector<uint8_t> read_memory(size_t start, size_t size);
uint32_t read_memory_word(size_t pos); void read_mem(uint8_t *dst, size_t addr, size_t size);
uint16_t read_memory_half_word(size_t pos); void write_mem(uint8_t *src, size_t addr, size_t size);
uint8_t read_memory_byte(size_t pos);
void write_memory_word(size_t pos, uint32_t value);
void write_memory_half_word(size_t pos, uint16_t value);
void write_memory_byte(size_t pos, uint8_t value);
bool is_mmap(size_t pos, size_t size); bool is_mmap(size_t pos, size_t size);