massive feat: added a GDB stub for debugging
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2
.github/workflows/clang.yml
vendored
2
.github/workflows/clang.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
|
||||
- name: setup
|
||||
run: nix develop .#matar-clang -c meson setup $BUILDDIR
|
||||
run: nix develop .#matar-clang -c meson setup $BUILDDIR -Dgdb_debug=true
|
||||
|
||||
- name: fmt
|
||||
run: nix develop .#matar-clang -c ninja clang-format-check -C $BUILDDIR
|
||||
|
2
.github/workflows/gcc.yml
vendored
2
.github/workflows/gcc.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
|
||||
- name: setup
|
||||
run: nix develop .#matar -c meson setup $BUILDDIR
|
||||
run: nix develop .#matar -c meson setup $BUILDDIR -Dgdb_debug=true
|
||||
|
||||
- name: build
|
||||
run: nix develop .#matar -c ninja -C $BUILDDIR
|
||||
|
@@ -31,7 +31,6 @@ class Bus {
|
||||
|
||||
uint32_t read_word(uint32_t, bool = true);
|
||||
void write_word(uint32_t, uint32_t, bool = true);
|
||||
|
||||
// not sure what else to do?
|
||||
inline void internal_cycle() { cycles++; }
|
||||
|
||||
|
@@ -4,11 +4,19 @@
|
||||
#include "bus.hh"
|
||||
#include "cpu/psr.hh"
|
||||
#include "thumb/instruction.hh"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <cstdint>
|
||||
#ifdef GDB_DEBUG
|
||||
#include <unordered_set>
|
||||
#endif
|
||||
|
||||
namespace matar {
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
class GdbRsp;
|
||||
#endif
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(std::shared_ptr<Bus>) noexcept;
|
||||
@@ -16,24 +24,12 @@ class Cpu {
|
||||
void step();
|
||||
void chg_mode(const Mode to);
|
||||
|
||||
bool is_flushed = false;
|
||||
inline void flush_pipeline() {
|
||||
is_flushed = true;
|
||||
if (cpsr.state() == State::Arm) {
|
||||
opcodes[0] = bus->read_word(pc, false);
|
||||
advance_pc_arm();
|
||||
opcodes[1] = bus->read_word(pc);
|
||||
advance_pc_arm();
|
||||
} else {
|
||||
opcodes[0] = bus->read_halfword(pc, false);
|
||||
advance_pc_thumb();
|
||||
opcodes[1] = bus->read_halfword(pc);
|
||||
advance_pc_thumb();
|
||||
}
|
||||
sequential = true;
|
||||
};
|
||||
inline bool is_halted() { return halted; }
|
||||
inline void resume() { halted = false; }
|
||||
|
||||
private:
|
||||
bool halted = false;
|
||||
|
||||
friend void arm::Instruction::exec(Cpu& cpu);
|
||||
friend void thumb::Instruction::exec(Cpu& cpu);
|
||||
|
||||
@@ -93,5 +89,26 @@ class Cpu {
|
||||
|
||||
inline void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; };
|
||||
inline void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; }
|
||||
|
||||
bool is_flushed = false;
|
||||
inline void flush_pipeline() {
|
||||
if (cpsr.state() == State::Arm) {
|
||||
opcodes[0] = bus->read_word(pc, false);
|
||||
advance_pc_arm();
|
||||
opcodes[1] = bus->read_word(pc);
|
||||
advance_pc_arm();
|
||||
} else {
|
||||
opcodes[0] = bus->read_halfword(pc, false);
|
||||
advance_pc_thumb();
|
||||
opcodes[1] = bus->read_halfword(pc);
|
||||
advance_pc_thumb();
|
||||
}
|
||||
sequential = true;
|
||||
};
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
friend class GdbRsp;
|
||||
std::unordered_set<uint32_t> breakpoints = {};
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
10
meson.build
10
meson.build
@@ -7,8 +7,18 @@ project('matar', 'cpp',
|
||||
'cpp_std=c++23',
|
||||
'default_library=static'])
|
||||
|
||||
lib_cpp_args = []
|
||||
compiler = meson.get_compiler('cpp')
|
||||
|
||||
if get_option('disassembler')
|
||||
lib_cpp_args += '-DDISASSEMBLER'
|
||||
endif
|
||||
|
||||
if get_option('gdb_debug')
|
||||
lib_cpp_args += '-DGDB_DEBUG'
|
||||
endif
|
||||
|
||||
|
||||
subdir('include')
|
||||
subdir('src')
|
||||
subdir('apps')
|
||||
|
@@ -1,2 +1,3 @@
|
||||
option('tests', type : 'boolean', value : true, description: 'enable tests')
|
||||
option('disassembler', type: 'boolean', value: true, description: 'enable disassembler')
|
||||
option('gdb_debug', type: 'boolean', value: false, description: 'enable GDB RSP server')
|
||||
|
@@ -136,6 +136,7 @@ Bus::write(uint32_t address) {
|
||||
#undef MATCHES
|
||||
}
|
||||
|
||||
glogger.error("Invalid memory region written");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@@ -129,6 +129,14 @@ Cpu::step() {
|
||||
// word align
|
||||
rst_bit(pc, 1);
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
if (breakpoints.contains(pc - 2 * arm::INSTRUCTION_SIZE)) {
|
||||
glogger.info_bold("CPU halted");
|
||||
halted = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
arm::Instruction instruction(opcodes[0]);
|
||||
|
||||
opcodes[0] = opcodes[1];
|
||||
@@ -148,6 +156,15 @@ Cpu::step() {
|
||||
} else
|
||||
advance_pc_arm();
|
||||
} else {
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
if (breakpoints.contains(pc - 2 * thumb::INSTRUCTION_SIZE)) {
|
||||
glogger.info_bold("CPU halted");
|
||||
halted = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
thumb::Instruction instruction(opcodes[0]);
|
||||
|
||||
opcodes[0] = opcodes[1];
|
||||
|
481
src/gdb_rsp.cc
Normal file
481
src/gdb_rsp.cc
Normal file
@@ -0,0 +1,481 @@
|
||||
#include "gdb_rsp.hh"
|
||||
#include "util/log.hh"
|
||||
#include <csignal>
|
||||
#include <numeric>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace matar {
|
||||
|
||||
template<typename... Args>
|
||||
static inline constexpr void
|
||||
gdb_log(const std::format_string<Args...>& fmt, Args&&... args) {
|
||||
glogger.debug("GDB: {}", std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
static inline void
|
||||
append_le(std::string& str, uint32_t value) {
|
||||
// little endian only
|
||||
str += std::format("{:02x}", value & 0xFF);
|
||||
str += std::format("{:02x}", value >> 8 & 0xFF);
|
||||
str += std::format("{:02x}", value >> 16 & 0xFF);
|
||||
str += std::format("{:02x}", value >> 24 & 0xFF);
|
||||
}
|
||||
|
||||
static inline std::string
|
||||
be_to_le(std::string str) {
|
||||
if (str.length() != 8)
|
||||
throw std::out_of_range("string is supposed to be 8 bytes");
|
||||
|
||||
std::string current;
|
||||
|
||||
for (int i = 7; i >= 0; i -= 2) {
|
||||
current += str[i - 1];
|
||||
current += str[i];
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
GdbRsp::GdbRsp(std::shared_ptr<Cpu> cpu)
|
||||
: cpu(cpu) {}
|
||||
|
||||
void
|
||||
GdbRsp::start(const uint port) {
|
||||
server.start(port);
|
||||
server.run();
|
||||
|
||||
std::string msg;
|
||||
|
||||
while (!attached) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::satisfy_client() {
|
||||
while (server.client_waiting() && attached) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::step() {
|
||||
std::string msg = receive();
|
||||
|
||||
switch (msg[0]) {
|
||||
case '+':
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
case '\x03':
|
||||
gdb_log("ctrl+c interrupt received");
|
||||
cmd_halted();
|
||||
break;
|
||||
case '$': {
|
||||
if (msg.starts_with("$qSupported")) {
|
||||
acknowledge();
|
||||
cmd_supported(msg);
|
||||
} else if (msg.starts_with("$qAttached")) {
|
||||
acknowledge();
|
||||
cmd_attached();
|
||||
} else {
|
||||
switch (msg[1]) {
|
||||
case '?':
|
||||
acknowledge();
|
||||
cmd_halted();
|
||||
break;
|
||||
case 'g':
|
||||
acknowledge();
|
||||
cmd_read_registers();
|
||||
break;
|
||||
case 'G':
|
||||
acknowledge();
|
||||
cmd_write_registers(msg);
|
||||
break;
|
||||
case 'p':
|
||||
acknowledge();
|
||||
cmd_read_register(msg);
|
||||
break;
|
||||
case 'P':
|
||||
acknowledge();
|
||||
cmd_write_register(msg);
|
||||
break;
|
||||
case 'm':
|
||||
acknowledge();
|
||||
cmd_read_memory(msg);
|
||||
break;
|
||||
case 'M':
|
||||
acknowledge();
|
||||
cmd_write_memory(msg);
|
||||
break;
|
||||
case 'z':
|
||||
acknowledge();
|
||||
cmd_rm_breakpoint(msg);
|
||||
break;
|
||||
case 'Z':
|
||||
acknowledge();
|
||||
cmd_add_breakpoint(msg);
|
||||
break;
|
||||
case 'c':
|
||||
acknowledge();
|
||||
cmd_continue();
|
||||
break;
|
||||
case 'D':
|
||||
acknowledge();
|
||||
cmd_detach();
|
||||
break;
|
||||
default:
|
||||
gdb_log("unknown command");
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gdb_log("unknown message received");
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
GdbRsp::receive() {
|
||||
std::string msg = server.receive(1);
|
||||
char ch = msg[0];
|
||||
int checksum = 0;
|
||||
|
||||
if (ch == '$') {
|
||||
while ((ch = server.receive(1)[0]) != '#') {
|
||||
checksum += static_cast<uint>(ch);
|
||||
msg += ch;
|
||||
if (msg.length() > MAX_MSG_LEN) {
|
||||
throw std::logic_error("GDB: received message is too long");
|
||||
}
|
||||
}
|
||||
|
||||
if (std::stoul(server.receive(2), nullptr, 16) != (checksum & 0xFF)) {
|
||||
gdb_log("{}", msg);
|
||||
throw std::logic_error("GDB: bad message checksum");
|
||||
}
|
||||
}
|
||||
|
||||
gdb_log("received message \"{}\"", msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::string
|
||||
GdbRsp::make_packet(std::string raw) {
|
||||
uint checksum = std::accumulate(raw.begin(), raw.end(), 0);
|
||||
return std::format("${}#{:02x}", raw, checksum & 0xFF);
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::acknowledge() {
|
||||
server.send("+");
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::send_empty() {
|
||||
acknowledge();
|
||||
server.send(make_packet(""));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::notify_breakpoint_reached() {
|
||||
gdb_log("reached breakpoint, sending signal");
|
||||
server.send(make_packet(std::format("S{:02x}", SIGTRAP)));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_attached() {
|
||||
attached = true;
|
||||
|
||||
gdb_log("server is now attached");
|
||||
server.send(make_packet("1"));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_supported(std::string msg) {
|
||||
std::string response;
|
||||
|
||||
if (msg.find("hwbreak+;") != std::string::npos)
|
||||
response += "hwbreak+;";
|
||||
|
||||
gdb_log("sending response for qSupported");
|
||||
server.send(make_packet(response));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_halted() {
|
||||
gdb_log("sending reason for upcoming halt");
|
||||
server.send(make_packet(std::format("S{:02x}", SIGTRAP)));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_read_registers() {
|
||||
std::string response;
|
||||
|
||||
for (int i = 0; i < cpu->GPR_COUNT - 1; i++)
|
||||
append_le(response, cpu->gpr[i]);
|
||||
|
||||
// for some reason this PC needs to be the address of executing instruction
|
||||
// i.e, two instructions behind actual PC
|
||||
append_le(response,
|
||||
cpu->pc - 2 * (cpu->cpsr.state() == State::Arm
|
||||
? arm::INSTRUCTION_SIZE
|
||||
: thumb::INSTRUCTION_SIZE));
|
||||
|
||||
gdb_log("sending register values");
|
||||
server.send(make_packet(response));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_write_registers(std::string msg) {
|
||||
static std::regex rgx("\\$G([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
|
||||
if (sm.size() != 2 || sm[1].str().size() != 16 * 8) {
|
||||
gdb_log("invalid arguments to write registers");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::string values = sm[1].str();
|
||||
|
||||
for (uint i = 0, j = 0; i < values.length() - 8; i += 8, j++) {
|
||||
cpu->gpr[i] = std::stoul(sm[i + 1].str(), nullptr, 16);
|
||||
cpu->gpr[j] =
|
||||
std::stoul(be_to_le(values.substr(i, 8)), nullptr, 16);
|
||||
}
|
||||
|
||||
gdb_log("register values written");
|
||||
server.send(OK_MSG);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_read_register(std::string msg) {
|
||||
std::string response;
|
||||
|
||||
try {
|
||||
uint reg = std::stoul(msg.substr(2), nullptr, 16);
|
||||
// 25th register is CPSR in gdb ARM
|
||||
if (reg == 25)
|
||||
append_le(response, cpu->cpsr.raw());
|
||||
else if (reg < cpu->GPR_COUNT)
|
||||
append_le(response, cpu->gpr[reg]);
|
||||
else
|
||||
response += "xxxxxxxx";
|
||||
|
||||
gdb_log("sending single register value");
|
||||
server.send(make_packet(response));
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_write_register(std::string msg) {
|
||||
static std::regex rgx("\\$P([0-9A-Fa-f]+)\\=([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
|
||||
if (sm.size() != 3 && sm[2].str().length() != 8) {
|
||||
gdb_log("invalid arguments to write single register");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
uint reg = std::stoul(sm[1].str(), nullptr, 16);
|
||||
uint32_t value = std::stoul(be_to_le(sm[2].str()), nullptr, 16);
|
||||
|
||||
dbg(value);
|
||||
|
||||
if (reg == 25)
|
||||
cpu->cpsr.set_all(value);
|
||||
else if (reg < cpu->GPR_COUNT)
|
||||
cpu->gpr[reg] = value;
|
||||
|
||||
gdb_log("single register value written");
|
||||
server.send(OK_MSG);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_read_memory(std::string msg) {
|
||||
std::string response;
|
||||
bool sequential = false;
|
||||
|
||||
static std::regex rgx("\\$m([0-9A-Fa-f]+),([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
|
||||
if (sm.size() != 3) {
|
||||
gdb_log("invalid arguments to read memory");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t address = 0, length = 0;
|
||||
|
||||
try {
|
||||
address = std::stoul(sm[1].str(), nullptr, 16);
|
||||
length = std::stoul(sm[2].str(), nullptr, 16);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < length; i++) {
|
||||
response +=
|
||||
std::format("{:02x}", cpu->bus->read_byte(address + i), sequential);
|
||||
sequential = true;
|
||||
}
|
||||
|
||||
cpu->sequential = false;
|
||||
gdb_log("sending memory values values");
|
||||
server.send(make_packet(response));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_write_memory(std::string msg) {
|
||||
bool sequential = false;
|
||||
|
||||
static std::regex rgx("\\$M([0-9A-Fa-f]+),([0-9A-Fa-f]+):([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
|
||||
if (sm.size() != 4) {
|
||||
gdb_log("invalid arguments to write memory");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
uint32_t address = std::stoul(sm[1].str(), nullptr, 16);
|
||||
uint32_t length = std::stoul(sm[2].str(), nullptr, 16);
|
||||
|
||||
std::string values = sm[3].str();
|
||||
|
||||
for (uint i = 0, j = 0; i < length && j < values.size(); i++, j += 2) {
|
||||
cpu->bus->write_byte(address + i,
|
||||
std::stoul(values.substr(j, 2), nullptr, 16) &
|
||||
0xFF,
|
||||
sequential);
|
||||
glogger.warn("hi {:02x}", cpu->bus->read_byte(address + i));
|
||||
sequential = true;
|
||||
}
|
||||
|
||||
cpu->sequential = false;
|
||||
gdb_log("register values written");
|
||||
server.send(OK_MSG);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_rm_breakpoint(std::string msg) {
|
||||
static std::regex rgx("\\$z(0|1),([0-9A-Fa-f]+),(2|3|4)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
|
||||
if (sm.size() != 4) {
|
||||
gdb_log("invalid arguments to remove breakpoint");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sm[1].str() != "0" && sm[0].str() != "1") {
|
||||
gdb_log("unrecognized breakpoint type encountered");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sm[3].str() != "3" && sm[3].str() != "4") {
|
||||
gdb_log("only 32 bit breakpoints supported");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
uint32_t address = std::stoul(sm[2].str(), nullptr, 16);
|
||||
|
||||
cpu->breakpoints.erase(address);
|
||||
gdb_log("breakpoint {:#08x} removed", address);
|
||||
server.send(OK_MSG);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_add_breakpoint(std::string msg) {
|
||||
static std::regex rgx("\\$Z(0|1),([0-9A-Fa-f]+),(2|3|4)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
dbg(sm.size());
|
||||
dbg(sm[0].str());
|
||||
|
||||
if (sm.size() != 4) {
|
||||
gdb_log("invalid arguments to add breakpoint");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sm[1].str() != "0" && sm[0].str() != "1") {
|
||||
gdb_log("unrecognized breakpoint type encountered");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sm[3].str() != "3" && sm[3].str() != "4") {
|
||||
gdb_log("only 32 bit breakpoints supported");
|
||||
send_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
uint32_t address = std::stoul(sm[2].str(), nullptr, 16);
|
||||
|
||||
cpu->breakpoints.insert(address);
|
||||
gdb_log("breakpoint {:#08x} added", address);
|
||||
server.send(OK_MSG);
|
||||
} catch (const std::exception& e) {
|
||||
gdb_log("{}", e.what());
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_detach() {
|
||||
attached = false;
|
||||
cpu->resume();
|
||||
gdb_log("detached");
|
||||
server.send(OK_MSG);
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_continue() {
|
||||
cpu->resume();
|
||||
gdb_log("continued");
|
||||
server.send(OK_MSG);
|
||||
while (true) {
|
||||
cpu->step();
|
||||
}
|
||||
}
|
||||
}
|
43
src/gdb_rsp.hh
Normal file
43
src/gdb_rsp.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "cpu/cpu.hh"
|
||||
#include "util/tcp_server.hh"
|
||||
|
||||
namespace matar {
|
||||
class GdbRsp {
|
||||
public:
|
||||
GdbRsp(std::shared_ptr<Cpu> cpu);
|
||||
void start(const uint port);
|
||||
void satisfy_client();
|
||||
void step();
|
||||
|
||||
private:
|
||||
bool attached = false;
|
||||
|
||||
std::shared_ptr<Cpu> cpu;
|
||||
net::TcpServer server;
|
||||
std::string receive();
|
||||
std::string make_packet(std::string raw);
|
||||
void acknowledge();
|
||||
void send_empty();
|
||||
void notify_breakpoint_reached();
|
||||
|
||||
// Commands
|
||||
void cmd_attached();
|
||||
void cmd_supported(std::string msg);
|
||||
void cmd_halted();
|
||||
void cmd_read_registers();
|
||||
void cmd_write_registers(std::string msg);
|
||||
void cmd_read_register(std::string msg);
|
||||
void cmd_write_register(std::string msg);
|
||||
void cmd_read_memory(std::string msg);
|
||||
void cmd_write_memory(std::string msg);
|
||||
void cmd_rm_breakpoint(std::string msg);
|
||||
void cmd_add_breakpoint(std::string msg);
|
||||
void cmd_detach();
|
||||
void cmd_continue();
|
||||
|
||||
static constexpr std::string ATTACHED_MSG = "$qAttached#8f";
|
||||
static constexpr std::string OK_MSG = "+$OK#9a";
|
||||
|
||||
static constexpr uint MAX_MSG_LEN = 4096;
|
||||
};
|
||||
}
|
@@ -2,16 +2,14 @@ lib_sources = files(
|
||||
'bus.cc',
|
||||
)
|
||||
|
||||
if get_option('gdb_debug')
|
||||
lib_sources += files('gdb_rsp.cc')
|
||||
endif
|
||||
|
||||
subdir('util')
|
||||
subdir('cpu')
|
||||
subdir('io')
|
||||
|
||||
lib_cpp_args = []
|
||||
|
||||
if get_option('disassembler')
|
||||
lib_cpp_args += '-DDISASSEMBLER'
|
||||
endif
|
||||
|
||||
lib = library(
|
||||
meson.project_name(),
|
||||
lib_sources,
|
||||
|
@@ -1,3 +1,8 @@
|
||||
lib_sources += files(
|
||||
'log.cc'
|
||||
'log.cc',
|
||||
'tcp_server.cc'
|
||||
)
|
||||
|
||||
if get_option('gdb_debug')
|
||||
lib_sources += files('tcp_server.cc')
|
||||
endif
|
80
src/util/tcp_server.cc
Normal file
80
src/util/tcp_server.cc
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "tcp_server.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace net {
|
||||
TcpServer::TcpServer()
|
||||
: server_fd(0)
|
||||
, client_fd(0) {}
|
||||
|
||||
TcpServer::~TcpServer() {
|
||||
close(server_fd);
|
||||
close(client_fd);
|
||||
}
|
||||
|
||||
bool
|
||||
TcpServer::client_waiting() {
|
||||
int count = 0;
|
||||
ioctl(client_fd, FIONREAD, &count);
|
||||
return static_cast<bool>(count);
|
||||
}
|
||||
|
||||
void
|
||||
TcpServer::run() {
|
||||
socklen_t cli_addr_size = sizeof(client_addr);
|
||||
|
||||
client_fd = ::accept(
|
||||
server_fd, reinterpret_cast<sockaddr*>(&client_addr), &cli_addr_size);
|
||||
|
||||
if (client_fd == -1)
|
||||
throw std::runtime_error("accept failed");
|
||||
}
|
||||
|
||||
void
|
||||
TcpServer::start(uint port) {
|
||||
server_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd == -1) {
|
||||
throw std::runtime_error("creating socket failed");
|
||||
}
|
||||
|
||||
int option = 1;
|
||||
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
|
||||
|
||||
std::memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = PF_INET;
|
||||
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (::bind(server_fd,
|
||||
reinterpret_cast<sockaddr*>(&server_addr),
|
||||
sizeof(server_addr)) == -1) {
|
||||
throw std::runtime_error("binding socket failed");
|
||||
}
|
||||
if (::listen(server_fd, 1) == -1) {
|
||||
throw std::runtime_error("listening failed");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpServer::send(std::string msg) {
|
||||
if (::send(client_fd, msg.data(), msg.length(), 0) == -1) {
|
||||
throw std::runtime_error(
|
||||
std::format("failed to send message: {}\n", strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
TcpServer::receive(uint length) {
|
||||
char msg[MAX_PACKET_SIZE];
|
||||
ssize_t num_bytes = recv(client_fd, msg, length, 0);
|
||||
msg[length] = '\0';
|
||||
if (num_bytes < 0) {
|
||||
throw std::runtime_error(
|
||||
std::format("failed to receive messages: {}\n", strerror(errno)));
|
||||
}
|
||||
return std::string(msg);
|
||||
}
|
||||
}
|
26
src/util/tcp_server.hh
Normal file
26
src/util/tcp_server.hh
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <string>
|
||||
|
||||
namespace net {
|
||||
|
||||
class TcpServer {
|
||||
public:
|
||||
TcpServer();
|
||||
~TcpServer();
|
||||
|
||||
void run();
|
||||
void start(uint port);
|
||||
void send(std::string msg);
|
||||
std::string receive(uint length);
|
||||
bool client_waiting();
|
||||
|
||||
private:
|
||||
static constexpr uint MAX_PACKET_SIZE = 4096;
|
||||
int server_fd;
|
||||
int client_fd;
|
||||
sockaddr_in server_addr;
|
||||
sockaddr_in client_addr;
|
||||
};
|
||||
}
|
@@ -9,15 +9,11 @@ tests_sources = files(
|
||||
'bus.cc'
|
||||
)
|
||||
|
||||
tests_cpp_args = lib_cpp_args
|
||||
|
||||
subdir('cpu')
|
||||
subdir('util')
|
||||
|
||||
tests_cpp_args = []
|
||||
|
||||
if get_option('disassembler')
|
||||
tests_cpp_args += '-DDISASSEMBLER'
|
||||
endif
|
||||
|
||||
catch2 = dependency('catch2', version: '>=3.4.0', static: true)
|
||||
catch2_tests = executable(
|
||||
'matar_tests',
|
||||
|
Reference in New Issue
Block a user