13using namespace MaxOS::runtime;
14using namespace MaxOS::hardwarecommunication;
15using namespace MaxOS::common;
16using namespace MaxOS::console;
17using namespace MaxOS::processes;
18using namespace MaxOS::system;
19using namespace MaxOS::memory;
31 m_trap_flag_handler(this),
32 m_break_point_handler(this)
36GDBServer::~GDBServer() =
default;
49 m_attached_thread = thread;
57void GDBServer::handle_interrupt() {
61 current_command_buffer += m_serial->
read_char();
69void GDBServer::try_handle_command() {
72 int checksum_start = -1;
73 for (
int i = 0; i < current_command_buffer.length(); ++i)
74 if (current_command_buffer[i] ==
'#') {
80 if (checksum_start == -1 || current_command_buffer.length() < checksum_start + 3)
84 int command_start = -1;
85 for (
int i = 0; i < current_command_buffer.length(); ++i)
86 if (current_command_buffer[i] ==
'$') {
92 string command = current_command_buffer.substring(command_start + 1, checksum_start - command_start - 1);
93 string checksum = current_command_buffer.substring(checksum_start + 1, 2);
94 current_command_buffer = current_command_buffer.substring(checksum_start + 3, current_command_buffer.length() - checksum_start - 2);
103 delegate_command(command);
106 if (current_command_buffer.length() > 0)
107 try_handle_command();
115void GDBServer::delegate_command(
const string &command) {
118 if (command ==
"?") {
124 if (command ==
"s") {
130 if (command ==
"g") {
136 if (command ==
"c") {
148 if (command.starts_with(
"m")) {
154 if (command.starts_with(
"Z0")) {
160 if (command.starts_with(
"z0")) {
166 if (command.starts_with(
"qSupported")){
172 if (command.starts_with(
"qAttached")){
178 if (command.starts_with(
"qXfer")) {
179 send_message(
"l<target version=\"1.0\"><architecture>i386:x86-64</architecture></target>");
197string GDBServer::format_register(uint64_t value,
int num_bytes) {
200 constexpr char* HEX_CHARS =
"0123456789abcdef";
203 for (
int i = 0; i < num_bytes; ++i) {
206 uint8_t
byte = (value >> (i * 8)) & 0xFF;
209 out += String(HEX_CHARS[(
byte >> 4) & 0x0F]);
210 out += String(HEX_CHARS[
byte & 0x0F]);
225 uint8_t checksum = 0;
226 for (
int i = 0; i < payload.length(); ++i)
227 checksum += (uint8_t)payload[i];
233 string hex = string((uint64_t)checksum);
234 if (hex.length() == 1)
269 m_serial->write(message);
286 string out =
"PacketSize=4000;qXfer:features:read+;";
289 Vector<string> features = peer_supported.split(
"+;");
290 for (
const auto& feature : features)
291 if (feature ==
"hwbreak")
305 payload += format_register(thread_regs.rax, 8);
306 payload += format_register(thread_regs.rbx, 8);
307 payload += format_register(thread_regs.rcx, 8);
308 payload += format_register(thread_regs.rdx, 8);
309 payload += format_register(thread_regs.rsi, 8);
310 payload += format_register(thread_regs.rdi, 8);
311 payload += format_register(thread_regs.rbp, 8);
312 payload += format_register(thread_regs.rsp, 8);
315 payload += format_register(thread_regs.r8, 8);
316 payload += format_register(thread_regs.r9, 8);
317 payload += format_register(thread_regs.r10, 8);
318 payload += format_register(thread_regs.r11, 8);
319 payload += format_register(thread_regs.r12, 8);
320 payload += format_register(thread_regs.r13, 8);
321 payload += format_register(thread_regs.r14, 8);
322 payload += format_register(thread_regs.r15, 8);
325 payload += format_register(thread_regs.rip, 8);
328 payload += format_register(thread_regs.rflags, 4);
329 payload += format_register(thread_regs.cs, 4);
330 payload += format_register(thread_regs.ss, 4);
333 payload +=
"x"s * (8 * 4);
346 Vector<String> parts = message.substring(1,message.length( ) - 1).split(
",");
347 string address_str = parts[0].to_upper();
348 string length_str = parts[1].to_upper();
350 send_memory((
void*)address_str.hex_to_uint64(), length_str.hex_to_uint64());
362 if (address < (
void*)0x1000) {
372 String payload(address, length);
376 MemoryManager::switch_active_memory_manager(old);
389 thread_regs.rflags |= (1 << 8);
408 Vector<String> parts = message.substring(2,message.length( ) - 2).split(
",");
409 string address_str = parts[1].to_upper();
423 if (address < (
void*)0x1000) {
429 if (m_breakpoints.find(address) != m_breakpoints.end()) {
439 uint8_t old_byte = *(uint8_t*)address;
440 m_breakpoints.insert(address, old_byte);
443 CPU::disable_write_protect();
444 *(uint8_t*)address = 0xCC;
445 CPU::enable_write_protect();
448 MemoryManager::switch_active_memory_manager(old);
460 Vector<String> parts = message.substring(2,message.length( ) - 2).split(
",");
461 string address_str = parts[1].to_upper();
475 if (address < (
void*)0x1000) {
481 auto breakpoint = m_breakpoints.find(address);
482 if (breakpoint == m_breakpoints.end()) {
492 CPU::disable_write_protect();
493 *(uint8_t*)address = breakpoint->second;
494 CPU::enable_write_protect();
497 m_breakpoints.erase(breakpoint);
500 MemoryManager::switch_active_memory_manager(old);
523 for (
const auto& bp : m_breakpoints)
538 return m_attached_thread;
549 m_gdb_handler(server)
554TrapFlagExcpetion::~TrapFlagExcpetion() =
default;
565 status->rflags &= ~(1 << 8);
566 m_gdb_handler->thread_regs = *status;
576 *status = m_gdb_handler->thread_regs;
587 m_gdb_handler(server)
592BreakPointException::~BreakPointException() =
default;
604 m_gdb_handler->thread_regs = *status;
614 *status = m_gdb_handler->thread_regs;
static LogRecord DEBUG()
Gets the active logger for debug logs.
A driver for the serial output.
char read_char()
Read data from the serial port.
void write_char(char c) final
Waits for the serial port to be ready, then writes a character to it.
bool can_read()
Checks if data can be read from the serial port.
Base class for handing CPU expection interrupts.
Handles a certain interrupt number.
virtual void handle_interrupt()
Handles an interrupt.
Handles memory allocation and deallocation.
static Process * get_process(uint64_t pid)
Gets a process on the currently executing core by its PID.
static Process * current_process()
Gets the process on the currently executing core.
static Thread * current_thread()
Gets the thread on the currently executing core.
memory::MemoryManager * memory_manager
The manager for memory used by this process.
The execution context of a sub-process thread.
uint64_t parent_pid
The parent process ID.
volatile system::cpu_status_t execution_state
The CPU state of the thread.
void yield()
Pass execution to the next ready thread.
volatile thread_state_t thread_state
The current state of the thread.
BreakPointException(GDBServer *server)
Registers a handler for the BreakPointException (interrupt 0x1)
Handler for running a GDB server over serial.
processes::Thread * attached_thread()
Get the attached thread.
GDBServer(console::SerialConsole *serial)
Constructs a handler for remote debugging using GDB, also sets up interrupt handlers for traps and br...
void handle_step_instruction()
Set the trap flag and allow the thread to run.
string build_message(const string &payload)
Converts a message into the form GDB expects, including and ACK package.
void send_message(const string &payload)
Converts a message to the required form and sends it over serial.
void handle_set_breakpoint(const string &message)
Set a software break point by replacing the instruction with a INT3.
string get_checksum(const string &payload)
Calculates the GDB checksum of a command.
bool check_checksum(const string &payload, const string &checksum)
Validates the checksum of a commnad.
void send_supported(const string &peer_supported)
Sends the features that this GDB stub supports to the GDB server.
void send_memory(const string &message)
Send a chunk of memory to the GDB server.
void handle_remove_breakpoint(const string &message)
Remove a software break point, and replace the original opcode.
void handle_continue()
Continues the exeuction of the thread, acknowledges the request and allows the thread to run.
void send_empty()
Send an empty message to the GDB server.
void send_registers()
Sends the registers of the thread to the GDB server.
void attach(processes::Thread *thread)
Begin debugging a thread, will start paused.
void handle_detach()
Detaches the thread from the GDB, removing all breakpoints and allowing it to be scheduled as usual.
TrapFlagExcpetion(GDBServer *server)
Registers a handler for the TrapFlagException (interrupt 0x1)
struct PACKED MaxOS::system::CPUStatus cpu_status_t
Alias for CPUStatus struct.
GDB Stub definition for remote debugging userspace processes.
Defines a GlobalScheduler and Scheduler for managing processes and threads.