X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/b91be0033265b5b6bd716bd39cb0971da41906c4..7804c2dae30700296c3205aaf7f546f491999bf4:/lunaix-os/kernel/debug/gdbstub.c diff --git a/lunaix-os/kernel/debug/gdbstub.c b/lunaix-os/kernel/debug/gdbstub.c new file mode 100644 index 0000000..38d35ad --- /dev/null +++ b/lunaix-os/kernel/debug/gdbstub.c @@ -0,0 +1,1325 @@ +/** + * @file gdbstub.h + * @author Lunaixsky () + * @brief GDB Stub implementation (https://github.com/mborgerson/gdbstub). + * Ported for LunaixOS + * @version 0.1 + * @date 2022-10-18 + * + * @copyright Copyright (c) 2022 Lunaixsky & (See license below) + * + */ + +/* + * Copyright (c) 2016-2022 Matt Borgerson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +/***************************************************************************** + * Types + ****************************************************************************/ + +#ifndef GDBSTUB_DONT_DEFINE_STDINT_TYPES +typedef unsigned char u8_t; +typedef unsigned short u16_t; +typedef unsigned long uint32_t; +#endif + +typedef unsigned int address; +typedef unsigned int reg; + +enum GDB_REGISTER +{ + GDB_CPU_I386_REG_EAX = 0, + GDB_CPU_I386_REG_ECX = 1, + GDB_CPU_I386_REG_EDX = 2, + GDB_CPU_I386_REG_EBX = 3, + GDB_CPU_I386_REG_ESP = 4, + GDB_CPU_I386_REG_EBP = 5, + GDB_CPU_I386_REG_ESI = 6, + GDB_CPU_I386_REG_EDI = 7, + GDB_CPU_I386_REG_PC = 8, + GDB_CPU_I386_REG_PS = 9, + GDB_CPU_I386_REG_CS = 10, + GDB_CPU_I386_REG_SS = 11, + GDB_CPU_I386_REG_DS = 12, + GDB_CPU_I386_REG_ES = 13, + GDB_CPU_I386_REG_FS = 14, + GDB_CPU_I386_REG_GS = 15, + GDB_CPU_NUM_REGISTERS = 16 +}; + +struct gdb_state +{ + int signum; + reg registers[GDB_CPU_NUM_REGISTERS]; +}; + +/***************************************************************************** + * + * GDB Remote Serial Protocol + * + ****************************************************************************/ + +/***************************************************************************** + * Macros + ****************************************************************************/ + +#define GDB_PRINT(...) + +#define COM_PORT SERIAL_COM1 + +#define GDB_EOF (-1) + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef GDB_ASSERT +#define GDB_ASSERT(x) \ + do { \ + } while (0) +#endif + +/***************************************************************************** + * Prototypes + ****************************************************************************/ + +int +gdb_main(struct gdb_state* state); + +/* System functions, supported by all stubs */ +void +gdb_sys_init(void); +int +gdb_sys_getc(struct gdb_state* state); +int +gdb_sys_putchar(struct gdb_state* state, int ch); +int +gdb_sys_mem_readb(struct gdb_state* state, address addr, char* val); +int +gdb_sys_mem_writeb(struct gdb_state* state, address addr, char val); +int +gdb_sys_continue(struct gdb_state* state); +int +gdb_sys_step(struct gdb_state* state); + +/***************************************************************************** + * Types + ****************************************************************************/ + +typedef int (*gdb_enc_func)(char* buf, + unsigned int buf_len, + const char* data, + unsigned int data_len); +typedef int (*gdb_dec_func)(const char* buf, + unsigned int buf_len, + char* data, + unsigned int data_len); + +/***************************************************************************** + * Const Data + ****************************************************************************/ + +static const char digits[] = "0123456789abcdef"; + +/***************************************************************************** + * Prototypes + ****************************************************************************/ + +/* Communication functions */ +static int +gdb_write(struct gdb_state* state, const char* buf, unsigned int len); +static int +gdb_read(struct gdb_state* state, + char* buf, + unsigned int buf_len, + unsigned int len); + +/* String processing helper functions */ +static int +gdb_strlen(const char* ch); +static char +gdb_get_digit(int val); +static int +gdb_get_val(char digit, int base); +static int +gdb_strtol(const char* str, unsigned int len, int base, const char** endptr); + +/* Packet functions */ +static int +gdb_send_packet(struct gdb_state* state, const char* pkt, unsigned int pkt_len); +static int +gdb_recv_packet(struct gdb_state* state, + char* pkt_buf, + unsigned int pkt_buf_len, + unsigned int* pkt_len); +static int +gdb_checksum(const char* buf, unsigned int len); +static int +gdb_recv_ack(struct gdb_state* state); + +/* Data encoding/decoding */ +static int +gdb_enc_hex(char* buf, + unsigned int buf_len, + const char* data, + unsigned int data_len); +static int +gdb_dec_hex(const char* buf, + unsigned int buf_len, + char* data, + unsigned int data_len); +static int +gdb_enc_bin(char* buf, + unsigned int buf_len, + const char* data, + unsigned int data_len); +static int +gdb_dec_bin(const char* buf, + unsigned int buf_len, + char* data, + unsigned int data_len); + +/* Packet creation helpers */ +static int +gdb_send_ok_packet(struct gdb_state* state, char* buf, unsigned int buf_len); +static int +gdb_send_conmsg_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + const char* msg); +static int +gdb_send_signal_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + char signal); +static int +gdb_send_error_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + char error); + +/* Command functions */ +static int +gdb_mem_read(struct gdb_state* state, + char* buf, + unsigned int buf_len, + address addr, + unsigned int len, + gdb_enc_func enc); +static int +gdb_mem_write(struct gdb_state* state, + const char* buf, + unsigned int buf_len, + address addr, + unsigned int len, + gdb_dec_func dec); +static int +gdb_continue(struct gdb_state* state); +static int +gdb_step(struct gdb_state* state); + +/***************************************************************************** + * String Processing Helper Functions + ****************************************************************************/ + +/* + * Get null-terminated string length. + */ +static int +gdb_strlen(const char* ch) +{ + int len; + + len = 0; + while (*ch++) { + len += 1; + } + + return len; +} + +/* + * Get integer value for a string representation. + * + * If the string starts with + or -, it will be signed accordingly. + * + * If base == 0, the base will be determined: + * base 16 if the string starts with 0x or 0X, + * base 10 otherwise + * + * If endptr is specified, it will point to the last non-digit in the + * string. If there are no digits in the string, it will be set to NULL. + */ +static int +gdb_strtol(const char* str, unsigned int len, int base, const char** endptr) +{ + unsigned int pos; + int sign, tmp, value, valid; + + value = 0; + pos = 0; + sign = 1; + valid = 0; + + if (endptr) { + *(endptr) = 0; + } + + if (len < 1) { + return 0; + } + + /* Detect negative numbers */ + if (str[pos] == '-') { + sign = -1; + pos += 1; + } else if (str[pos] == '+') { + sign = 1; + pos += 1; + } + + /* Detect '0x' hex prefix */ + if ((pos + 2 < len) && (str[pos] == '0') && + ((str[pos + 1] == 'x') || (str[pos + 1] == 'X'))) { + base = 16; + pos += 2; + } + + if (base == 0) { + base = 10; + } + + for (; (pos < len) && (str[pos] != '\x00'); pos++) { + tmp = gdb_get_val(str[pos], base); + if (tmp == GDB_EOF) { + break; + } + + value = value * base + tmp; + valid = 1; /* At least one digit is valid */ + } + + if (!valid) { + return 0; + } + + if (endptr) { + *endptr = str + pos; + } + + value *= sign; + + return value; +} + +/* + * Get the corresponding ASCII hex digit character for a value. + */ +static char +gdb_get_digit(int val) +{ + if ((val >= 0) && (val <= 0xf)) { + return digits[val]; + } else { + return GDB_EOF; + } +} + +/* + * Get the corresponding value for a ASCII digit character. + * + * Supports bases 2-16. + */ +static int +gdb_get_val(char digit, int base) +{ + int value; + + if ((digit >= '0') && (digit <= '9')) { + value = digit - '0'; + } else if ((digit >= 'a') && (digit <= 'f')) { + value = digit - 'a' + 0xa; + } else if ((digit >= 'A') && (digit <= 'F')) { + value = digit - 'A' + 0xa; + } else { + return GDB_EOF; + } + + return (value < base) ? value : GDB_EOF; +} + +/***************************************************************************** + * Packet Functions + ****************************************************************************/ + +/* + * Receive a packet acknowledgment + * + * Returns: + * 0 if an ACK (+) was received + * 1 if a NACK (-) was received + * GDB_EOF otherwise + */ +static int +gdb_recv_ack(struct gdb_state* state) +{ + int response; + + /* Wait for packet ack */ + switch (response = gdb_sys_getc(state)) { + case '+': + /* Packet acknowledged */ + return 0; + case '-': + /* Packet negative acknowledged */ + return 1; + default: + /* Bad response! */ + GDB_PRINT("received bad packet response: 0x%2x\n", response); + return GDB_EOF; + } +} + +/* + * Calculate 8-bit checksum of a buffer. + * + * Returns: + * 8-bit checksum. + */ +static int +gdb_checksum(const char* buf, unsigned int len) +{ + unsigned char csum; + + csum = 0; + + while (len--) { + csum += *buf++; + } + + return csum; +} + +/* + * Transmits a packet of data. + * Packets are of the form: $# + * + * Returns: + * 0 if the packet was transmitted and acknowledged + * 1 if the packet was transmitted but not acknowledged + * GDB_EOF otherwise + */ +static int +gdb_send_packet(struct gdb_state* state, + const char* pkt_data, + unsigned int pkt_len) +{ + char buf[3]; + char csum; + + /* Send packet start */ + if (gdb_sys_putchar(state, '$') == GDB_EOF) { + return GDB_EOF; + } + + /* Send packet data */ + if (gdb_write(state, pkt_data, pkt_len) == GDB_EOF) { + return GDB_EOF; + } + + /* Send the checksum */ + buf[0] = '#'; + csum = gdb_checksum(pkt_data, pkt_len); + if ((gdb_enc_hex(buf + 1, sizeof(buf) - 1, &csum, 1) == GDB_EOF) || + (gdb_write(state, buf, sizeof(buf)) == GDB_EOF)) { + return GDB_EOF; + } + + return gdb_recv_ack(state); +} + +/* + * Receives a packet of data, assuming a 7-bit clean connection. + * + * Returns: + * 0 if the packet was received + * GDB_EOF otherwise + */ +static int +gdb_recv_packet(struct gdb_state* state, + char* pkt_buf, + unsigned int pkt_buf_len, + unsigned int* pkt_len) +{ + int data; + char expected_csum, actual_csum; + char buf[2]; + + /* Wait for packet start */ + actual_csum = 0; + + while (1) { + data = gdb_sys_getc(state); + if (data == GDB_EOF) { + return GDB_EOF; + } else if (data == '$') { + /* Detected start of packet. */ + break; + } + } + + /* Read until checksum */ + *pkt_len = 0; + while (1) { + data = gdb_sys_getc(state); + + if (data == GDB_EOF) { + /* Error receiving character */ + return GDB_EOF; + } else if (data == '#') { + /* End of packet */ + break; + } else { + /* Check for space */ + if (*pkt_len >= pkt_buf_len) { + GDB_PRINT("packet buffer overflow\n"); + return GDB_EOF; + } + + /* Store character and update checksum */ + pkt_buf[(*pkt_len)++] = (char)data; + } + } + + /* Receive the checksum */ + if ((gdb_read(state, buf, sizeof(buf), 2) == GDB_EOF) || + (gdb_dec_hex(buf, 2, &expected_csum, 1) == GDB_EOF)) { + return GDB_EOF; + } + + /* Verify checksum */ + actual_csum = gdb_checksum(pkt_buf, *pkt_len); + if (actual_csum != expected_csum) { + /* Send packet nack */ + GDB_PRINT("received packet with bad checksum\n"); + gdb_sys_putchar(state, '-'); + return GDB_EOF; + } + + /* Send packet ack */ + gdb_sys_putchar(state, '+'); + return 0; +} + +/***************************************************************************** + * Data Encoding/Decoding + ****************************************************************************/ + +/* + * Encode data to its hex-value representation in a buffer. + * + * Returns: + * 0+ number of bytes written to buf + * GDB_EOF if the buffer is too small + */ +static int +gdb_enc_hex(char* buf, + unsigned int buf_len, + const char* data, + unsigned int data_len) +{ + unsigned int pos; + + if (buf_len < data_len * 2) { + /* Buffer too small */ + return GDB_EOF; + } + + for (pos = 0; pos < data_len; pos++) { + *buf++ = gdb_get_digit((data[pos] >> 4) & 0xf); + *buf++ = gdb_get_digit((data[pos]) & 0xf); + } + + return data_len * 2; +} + +/* + * Decode data from its hex-value representation to a buffer. + * + * Returns: + * 0 if successful + * GDB_EOF if the buffer is too small + */ +static int +gdb_dec_hex(const char* buf, + unsigned int buf_len, + char* data, + unsigned int data_len) +{ + unsigned int pos; + int tmp; + + if (buf_len != data_len * 2) { + /* Buffer too small */ + return GDB_EOF; + } + + for (pos = 0; pos < data_len; pos++) { + /* Decode high nibble */ + tmp = gdb_get_val(*buf++, 16); + if (tmp == GDB_EOF) { + /* Buffer contained junk. */ + GDB_ASSERT(0); + return GDB_EOF; + } + + data[pos] = tmp << 4; + + /* Decode low nibble */ + tmp = gdb_get_val(*buf++, 16); + if (tmp == GDB_EOF) { + /* Buffer contained junk. */ + GDB_ASSERT(0); + return GDB_EOF; + } + data[pos] |= tmp; + } + + return 0; +} + +/* + * Encode data to its binary representation in a buffer. + * + * Returns: + * 0+ number of bytes written to buf + * GDB_EOF if the buffer is too small + */ +static int +gdb_enc_bin(char* buf, + unsigned int buf_len, + const char* data, + unsigned int data_len) +{ + unsigned int buf_pos, data_pos; + + for (buf_pos = 0, data_pos = 0; data_pos < data_len; data_pos++) { + if (data[data_pos] == '$' || data[data_pos] == '#' || + data[data_pos] == '}' || data[data_pos] == '*') { + if (buf_pos + 1 >= buf_len) { + GDB_ASSERT(0); + return GDB_EOF; + } + buf[buf_pos++] = '}'; + buf[buf_pos++] = data[data_pos] ^ 0x20; + } else { + if (buf_pos >= buf_len) { + GDB_ASSERT(0); + return GDB_EOF; + } + buf[buf_pos++] = data[data_pos]; + } + } + + return buf_pos; +} + +/* + * Decode data from its bin-value representation to a buffer. + * + * Returns: + * 0+ if successful, number of bytes decoded + * GDB_EOF if the buffer is too small + */ +static int +gdb_dec_bin(const char* buf, + unsigned int buf_len, + char* data, + unsigned int data_len) +{ + unsigned int buf_pos, data_pos; + + for (buf_pos = 0, data_pos = 0; buf_pos < buf_len; buf_pos++) { + if (data_pos >= data_len) { + /* Output buffer overflow */ + GDB_ASSERT(0); + return GDB_EOF; + } + if (buf[buf_pos] == '}') { + /* The next byte is escaped! */ + if (buf_pos + 1 >= buf_len) { + /* There's an escape character, but no escaped character + * following the escape character. */ + GDB_ASSERT(0); + return GDB_EOF; + } + buf_pos += 1; + data[data_pos++] = buf[buf_pos] ^ 0x20; + } else { + data[data_pos++] = buf[buf_pos]; + } + } + + return data_pos; +} + +/***************************************************************************** + * Command Functions + ****************************************************************************/ + +/* + * Read from memory and encode into buf. + * + * Returns: + * 0+ number of bytes written to buf + * GDB_EOF if the buffer is too small + */ +static int +gdb_mem_read(struct gdb_state* state, + char* buf, + unsigned int buf_len, + address addr, + unsigned int len, + gdb_enc_func enc) +{ + char data[64]; + unsigned int pos; + + if (len > sizeof(data)) { + return GDB_EOF; + } + + /* Read from system memory */ + for (pos = 0; pos < len; pos++) { + if (gdb_sys_mem_readb(state, addr + pos, &data[pos])) { + /* Failed to read */ + return GDB_EOF; + } + } + + /* Encode data */ + return enc(buf, buf_len, data, len); +} + +/* + * Write to memory from encoded buf. + */ +static int +gdb_mem_write(struct gdb_state* state, + const char* buf, + unsigned int buf_len, + address addr, + unsigned int len, + gdb_dec_func dec) +{ + char data[64]; + unsigned int pos; + + if (len > sizeof(data)) { + return GDB_EOF; + } + + /* Decode data */ + if (dec(buf, buf_len, data, len) == GDB_EOF) { + return GDB_EOF; + } + + /* Write to system memory */ + for (pos = 0; pos < len; pos++) { + if (gdb_sys_mem_writeb(state, addr + pos, data[pos])) { + /* Failed to write */ + return GDB_EOF; + } + } + + return 0; +} + +/* + * Continue program execution at PC. + */ +int +gdb_continue(struct gdb_state* state) +{ + gdb_sys_continue(state); + return 0; +} + +/* + * Step one instruction. + */ +int +gdb_step(struct gdb_state* state) +{ + gdb_sys_step(state); + return 0; +} + +/***************************************************************************** + * Packet Creation Helpers + ****************************************************************************/ + +/* + * Send OK packet + */ +static int +gdb_send_ok_packet(struct gdb_state* state, char* buf, unsigned int buf_len) +{ + return gdb_send_packet(state, "OK", 2); +} + +/* + * Send a message to the debugging console (via O XX... packet) + */ +static int +gdb_send_conmsg_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + const char* msg) +{ + unsigned int size; + int status; + + if (buf_len < 2) { + /* Buffer too small */ + return GDB_EOF; + } + + buf[0] = 'O'; + status = gdb_enc_hex(&buf[1], buf_len - 1, msg, gdb_strlen(msg)); + if (status == GDB_EOF) { + return GDB_EOF; + } + size = 1 + status; + return gdb_send_packet(state, buf, size); +} + +/* + * Send a signal packet (S AA). + */ +static int +gdb_send_signal_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + char signal) +{ + unsigned int size; + int status; + + if (buf_len < 4) { + /* Buffer too small */ + return GDB_EOF; + } + + buf[0] = 'S'; + status = gdb_enc_hex(&buf[1], buf_len - 1, &signal, 1); + if (status == GDB_EOF) { + return GDB_EOF; + } + size = 1 + status; + return gdb_send_packet(state, buf, size); +} + +/* + * Send a error packet (E AA). + */ +static int +gdb_send_error_packet(struct gdb_state* state, + char* buf, + unsigned int buf_len, + char error) +{ + unsigned int size; + int status; + + if (buf_len < 4) { + /* Buffer too small */ + return GDB_EOF; + } + + buf[0] = 'E'; + status = gdb_enc_hex(&buf[1], buf_len - 1, &error, 1); + if (status == GDB_EOF) { + return GDB_EOF; + } + size = 1 + status; + return gdb_send_packet(state, buf, size); +} + +/***************************************************************************** + * Communication Functions + ****************************************************************************/ + +/* + * Write a sequence of bytes. + * + * Returns: + * 0 if successful + * GDB_EOF if failed to write all bytes + */ +static int +gdb_write(struct gdb_state* state, const char* buf, unsigned int len) +{ + while (len--) { + if (gdb_sys_putchar(state, *buf++) == GDB_EOF) { + return GDB_EOF; + } + } + + return 0; +} + +/* + * Read a sequence of bytes. + * + * Returns: + * 0 if successfully read len bytes + * GDB_EOF if failed to read all bytes + */ +static int +gdb_read(struct gdb_state* state, + char* buf, + unsigned int buf_len, + unsigned int len) +{ + char c; + + if (buf_len < len) { + /* Buffer too small */ + return GDB_EOF; + } + + while (len--) { + if ((c = gdb_sys_getc(state)) == GDB_EOF) { + return GDB_EOF; + } + *buf++ = c; + } + + return 0; +} + +/***************************************************************************** + * Main Loop + ****************************************************************************/ + +/* + * Main debug loop. Handles commands. + */ +int +gdb_main(struct gdb_state* state) +{ + address addr; + char pkt_buf[256]; + int status; + unsigned int length; + unsigned int pkt_len; + const char* ptr_next; + + gdb_send_signal_packet(state, pkt_buf, sizeof(pkt_buf), state->signum); + + while (1) { + /* Receive the next packet */ + status = gdb_recv_packet(state, pkt_buf, sizeof(pkt_buf), &pkt_len); + if (status == GDB_EOF) { + break; + } + + if (pkt_len == 0) { + /* Received empty packet.. */ + continue; + } + + ptr_next = pkt_buf; + + /* + * Handle one letter commands + */ + switch (pkt_buf[0]) { + +/* Calculate remaining space in packet from ptr_next position. */ +#define token_remaining_buf (pkt_len - (ptr_next - pkt_buf)) + +/* Expecting a seperator. If not present, go to error */ +#define token_expect_seperator(c) \ + { \ + if (!ptr_next || *ptr_next != c) { \ + goto error; \ + } else { \ + ptr_next += 1; \ + } \ + } + +/* Expecting an integer argument. If not present, go to error */ +#define token_expect_integer_arg(arg) \ + { \ + arg = gdb_strtol(ptr_next, token_remaining_buf, 16, &ptr_next); \ + if (!ptr_next) { \ + goto error; \ + } \ + } + + /* + * Read Registers + * Command Format: g + */ + case 'g': + /* Encode registers */ + status = gdb_enc_hex(pkt_buf, + sizeof(pkt_buf), + (char*)&(state->registers), + sizeof(state->registers)); + if (status == GDB_EOF) { + goto error; + } + pkt_len = status; + gdb_send_packet(state, pkt_buf, pkt_len); + break; + + /* + * Write Registers + * Command Format: G XX... + */ + case 'G': + status = gdb_dec_hex(pkt_buf + 1, + pkt_len - 1, + (char*)&(state->registers), + sizeof(state->registers)); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); + break; + + /* + * Read a Register + * Command Format: p n + */ + case 'p': + ptr_next += 1; + token_expect_integer_arg(addr); + + if (addr >= GDB_CPU_NUM_REGISTERS) { + goto error; + } + + /* Read Register */ + status = gdb_enc_hex(pkt_buf, + sizeof(pkt_buf), + (char*)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == GDB_EOF) { + goto error; + } + gdb_send_packet(state, pkt_buf, status); + break; + + /* + * Write a Register + * Command Format: P n...=r... + */ + case 'P': + ptr_next += 1; + token_expect_integer_arg(addr); + token_expect_seperator('='); + + if (addr < GDB_CPU_NUM_REGISTERS) { + status = gdb_dec_hex(ptr_next, + token_remaining_buf, + (char*)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == GDB_EOF) { + goto error; + } + } + gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); + break; + + /* + * Read Memory + * Command Format: m addr,length + */ + case 'm': + ptr_next += 1; + token_expect_integer_arg(addr); + token_expect_seperator(','); + token_expect_integer_arg(length); + + /* Read Memory */ + status = gdb_mem_read( + state, pkt_buf, sizeof(pkt_buf), addr, length, gdb_enc_hex); + if (status == GDB_EOF) { + goto error; + } + gdb_send_packet(state, pkt_buf, status); + break; + + /* + * Write Memory + * Command Format: M addr,length:XX.. + */ + case 'M': + ptr_next += 1; + token_expect_integer_arg(addr); + token_expect_seperator(','); + token_expect_integer_arg(length); + token_expect_seperator(':'); + + /* Write Memory */ + status = gdb_mem_write(state, + ptr_next, + token_remaining_buf, + addr, + length, + gdb_dec_hex); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); + break; + + /* + * Write Memory (Binary) + * Command Format: X addr,length:XX.. + */ + case 'X': + ptr_next += 1; + token_expect_integer_arg(addr); + token_expect_seperator(','); + token_expect_integer_arg(length); + token_expect_seperator(':'); + + /* Write Memory */ + status = gdb_mem_write(state, + ptr_next, + token_remaining_buf, + addr, + length, + gdb_dec_bin); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); + break; + + /* + * Continue, Kill (also treated as continue!) + * Command Format: c [addr] + */ + case 'c': + case 'C': + case 'k': + gdb_continue(state); + return 0; + + /* + * Single-step + * Command Format: s [addr] + */ + case 's': + case 'S': + gdb_step(state); + return 0; + + case '?': + gdb_send_signal_packet( + state, pkt_buf, sizeof(pkt_buf), state->signum); + break; + + /* + * Unsupported Command + */ + default: + gdb_send_packet(state, 0, 0); + } + + continue; + + error: + gdb_send_error_packet(state, pkt_buf, sizeof(pkt_buf), 0x00); + +#undef token_remaining_buf +#undef token_expect_seperator +#undef token_expect_integer_arg + } + + return 0; +} + +/***************************************************************************** + * Types + ****************************************************************************/ + +struct gdb_idtr +{ + u16_t len; + uint32_t offset; +} __attribute__((packed)); + +struct gdb_idt_gate +{ + u16_t offset_low; + u16_t segment; + u16_t flags; + u16_t offset_high; +} __attribute__((packed)); + +/***************************************************************************** + * Prototypes + ****************************************************************************/ +#define gdb_x86_io_write_8(port, val) io_outb(port, val) +#define gdb_x86_io_read_8(port) io_inb(port) + +#define gdb_x86_serial_getc() serial_rx_byte(COM_PORT) +#define gdb_x86_serial_putchar(ch) serial_tx_byte(COM_PORT, ch) + +#ifdef __STRICT_ANSI__ +#define asm __asm__ +#endif + +static struct gdb_state gdb_state; +static volatile int start_debugging = 0; + +/* + * Debug interrupt handler. + */ +void +gdbstub_loop(isr_param* param) +{ + /* Translate vector to signal */ + switch (param->execp->vector) { + case 1: + gdb_state.signum = 5; + break; + case 3: + gdb_state.signum = 5; + break; + default: + gdb_state.signum = 7; + } + + /* Load Registers */ + gdb_state.registers[GDB_CPU_I386_REG_EAX] = param->registers.eax; + gdb_state.registers[GDB_CPU_I386_REG_ECX] = param->registers.ecx; + gdb_state.registers[GDB_CPU_I386_REG_EDX] = param->registers.edx; + gdb_state.registers[GDB_CPU_I386_REG_EBX] = param->registers.ebx; + gdb_state.registers[GDB_CPU_I386_REG_ESP] = param->esp; + gdb_state.registers[GDB_CPU_I386_REG_EBP] = param->registers.ebp; + gdb_state.registers[GDB_CPU_I386_REG_ESI] = param->registers.esi; + gdb_state.registers[GDB_CPU_I386_REG_EDI] = param->registers.edi; + gdb_state.registers[GDB_CPU_I386_REG_PC] = param->execp->eip; + gdb_state.registers[GDB_CPU_I386_REG_CS] = param->execp->cs; + gdb_state.registers[GDB_CPU_I386_REG_PS] = param->execp->eflags; + gdb_state.registers[GDB_CPU_I386_REG_SS] = param->execp->ss; + gdb_state.registers[GDB_CPU_I386_REG_DS] = param->registers.ds; + gdb_state.registers[GDB_CPU_I386_REG_ES] = param->registers.es; + gdb_state.registers[GDB_CPU_I386_REG_FS] = param->registers.fs; + gdb_state.registers[GDB_CPU_I386_REG_GS] = param->registers.gs; + + gdb_main(&gdb_state); + + /* Restore Registers */ + param->registers.eax = gdb_state.registers[GDB_CPU_I386_REG_EAX]; + param->registers.ecx = gdb_state.registers[GDB_CPU_I386_REG_ECX]; + param->registers.edx = gdb_state.registers[GDB_CPU_I386_REG_EDX]; + param->registers.ebx = gdb_state.registers[GDB_CPU_I386_REG_EBX]; + param->esp = gdb_state.registers[GDB_CPU_I386_REG_ESP]; + param->registers.ebp = gdb_state.registers[GDB_CPU_I386_REG_EBP]; + param->registers.esi = gdb_state.registers[GDB_CPU_I386_REG_ESI]; + param->registers.edi = gdb_state.registers[GDB_CPU_I386_REG_EDI]; + param->execp->eip = gdb_state.registers[GDB_CPU_I386_REG_PC]; + param->execp->cs = gdb_state.registers[GDB_CPU_I386_REG_CS]; + param->execp->eflags = gdb_state.registers[GDB_CPU_I386_REG_PS]; + param->execp->ss = gdb_state.registers[GDB_CPU_I386_REG_SS]; + param->registers.ds = gdb_state.registers[GDB_CPU_I386_REG_DS]; + param->registers.es = gdb_state.registers[GDB_CPU_I386_REG_ES]; + param->registers.fs = gdb_state.registers[GDB_CPU_I386_REG_FS]; + param->registers.gs = gdb_state.registers[GDB_CPU_I386_REG_GS]; +} + +/***************************************************************************** + * Debugging System Functions + ****************************************************************************/ + +/* + * Write one character to the debugging stream. + */ +int +gdb_sys_putchar(struct gdb_state* state, int ch) +{ + gdb_x86_serial_putchar(ch); + return ch; +} + +/* + * Read one character from the debugging stream. + */ +int +gdb_sys_getc(struct gdb_state* state) +{ + return gdb_x86_serial_getc() & 0xff; +} + +/* + * Read one byte from memory. + */ +int +gdb_sys_mem_readb(struct gdb_state* state, address addr, char* val) +{ + *val = *(volatile char*)addr; + return 0; +} + +/* + * Write one byte to memory. + */ +int +gdb_sys_mem_writeb(struct gdb_state* state, address addr, char val) +{ + *(volatile char*)addr = val; + return 0; +} + +/* + * Continue program execution. + */ +int +gdb_sys_continue(struct gdb_state* state) +{ + gdb_state.registers[GDB_CPU_I386_REG_PS] &= ~(1 << 8); + return 0; +} + +/* + * Single step the next instruction. + */ +int +gdb_sys_step(struct gdb_state* state) +{ + gdb_state.registers[GDB_CPU_I386_REG_PS] |= 1 << 8; + return 0; +} \ No newline at end of file