Improve vmem handling a bit
This commit is contained in:
parent
2395b75e4d
commit
dfc9b2824e
4 changed files with 120 additions and 125 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
172
src/vm.cpp
172
src/vm.cpp
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> VM::read_memory(size_t start, size_t size) {
|
void UART::read_mem(uint8_t* dst, size_t addr, size_t size) {
|
||||||
if (start >= PROGRAM_ADDR) {
|
if (addr < UART_ADDR || addr + size > UART_ADDR + 8) {
|
||||||
start -= PROGRAM_ADDR;
|
throw std::runtime_error("Memory access out of bounds");
|
||||||
if (start + size > memory_.size()) {
|
}
|
||||||
return std::vector<uint8_t>(size, 0);
|
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;
|
||||||
}
|
}
|
||||||
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) {
|
void UART::write_mem(uint8_t* src, size_t addr, size_t size) {
|
||||||
if (pos >= PROGRAM_ADDR) {
|
if (addr < UART_ADDR || addr + size > UART_ADDR + 8) {
|
||||||
pos -= PROGRAM_ADDR;
|
|
||||||
if (pos + 3 >= memory_.size()) {
|
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
throw std::runtime_error("Memory access out of bounds");
|
||||||
}
|
}
|
||||||
return *(uint32_t*)&memory_[pos];
|
addr -= UART_ADDR;
|
||||||
} else {
|
for (size_t i = 0; i < size; i++) {
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
switch (addr + i) {
|
||||||
|
case UART_THR:
|
||||||
|
std::cout.put(static_cast<char>(*src));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t VM::read_memory_half_word(size_t pos) {
|
void VM::read_mem(uint8_t* dst, size_t addr, size_t size) {
|
||||||
if (pos >= PROGRAM_ADDR) {
|
if (addr >= PROGRAM_ADDR) {
|
||||||
pos -= PROGRAM_ADDR;
|
addr -= PROGRAM_ADDR;
|
||||||
if (pos + 1 >= memory_.size()) {
|
if (addr + size > memory_.size()) {
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
throw std::runtime_error("Memory access out of bounds");
|
||||||
}
|
}
|
||||||
|
std::memcpy(dst, &memory_[addr], size);
|
||||||
return *(uint16_t*)&memory_[pos];
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t VM::read_memory_byte(size_t pos) {
|
void VM::write_mem(uint8_t* src, size_t addr, size_t size) {
|
||||||
if (pos >= PROGRAM_ADDR) {
|
if (addr >= PROGRAM_ADDR) {
|
||||||
pos -= PROGRAM_ADDR;
|
addr -= PROGRAM_ADDR;
|
||||||
if (pos >= memory_.size()) {
|
if (addr + size > memory_.size()) {
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
throw std::runtime_error("Memory access out of bounds");
|
||||||
}
|
}
|
||||||
|
std::memcpy(&memory_[addr], src, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return memory_[pos];
|
if (is_mmap(addr, size)) {
|
||||||
} else {
|
if (addr >= UART_ADDR && addr < UART_ADDR + 8) {
|
||||||
throw std::runtime_error("Memory access out of bounds");
|
uart.write_mem(src, addr, size);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> VM::read_memory(size_t start, size_t size) {
|
||||||
|
std::vector<uint8_t> res(size, 0);
|
||||||
|
|
||||||
|
size_t i = start;
|
||||||
|
size_t end = start + size;
|
||||||
|
|
||||||
|
while (i < end) {
|
||||||
|
if (i % 4 == 0 && end - i >= 4) {
|
||||||
|
read_mem(&res[i - start], i, 4);
|
||||||
|
i += 4;
|
||||||
|
} else if (i % 2 == 0 && end - i >= 2) {
|
||||||
|
read_mem(&res[i - start], i, 2);
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
read_mem(&res[i - start], i, 1);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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*)®isters[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*)®isters[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*)®isters[rs2], addr, 4);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Unknown store instruction");
|
throw std::runtime_error("Unknown store instruction");
|
||||||
}
|
}
|
||||||
|
|
16
src/vm.hpp
16
src/vm.hpp
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue