From 54477ef8bb848e27aa4bf95f6a8a95e322c885e1 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Tue, 17 Dec 2019 21:21:25 -0700 Subject: Rename gdbstub_rsp.c to simply gdbstub.c --- Makefile | 2 +- gdbstub.c | 964 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdbstub_rsp.c | 964 ---------------------------------------------------------- 3 files changed, 965 insertions(+), 965 deletions(-) create mode 100644 gdbstub.c delete mode 100644 gdbstub_rsp.c diff --git a/Makefile b/Makefile index e956044..446f281 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ OBJCOPY = objcopy OBJCOPYFLAGS = --output-target=binary TARGET = gdbstub.bin BASE_ADDRESS = 0x500000 -OBJECTS = gdbstub_rsp.o \ +OBJECTS = gdbstub.o \ $(ARCH)/gdbstub_sys.o ifeq ($(ARCH),arch_x86) diff --git a/gdbstub.c b/gdbstub.c new file mode 100644 index 0000000..439bd4f --- /dev/null +++ b/gdbstub.c @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2016-2019 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 "gdbstub.h" + +/***************************************************************************** + * Types + ****************************************************************************/ + +typedef int (*dbg_enc_func)(char *buf, size_t buf_len, const char *data, size_t data_len); +typedef int (*dbg_dec_func)(const char *buf, size_t buf_len, char *data, size_t data_len); + +/***************************************************************************** + * Const Data + ****************************************************************************/ + +const char digits[] = "0123456789abcdef"; + +/***************************************************************************** + * Prototypes + ****************************************************************************/ + +/* Communication functions */ +int dbg_write(const char *buf, size_t len); +int dbg_read(char *buf, size_t buf_len, size_t len); + +/* String processing helper functions */ +int dbg_strlen(const char *ch); +int dbg_is_printable_char(char ch); +char dbg_get_digit(int val); +int dbg_get_val(char digit, int base); +int dbg_strtol(const char *str, size_t len, int base, const char **endptr); + +/* Packet functions */ +int dbg_send_packet(const char *pkt, size_t pkt_len); +int dbg_recv_packet(char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len); +int dbg_checksum(const char *buf, size_t len); +int dbg_recv_ack(void); + +/* Data encoding/decoding */ +int dbg_enc_hex(char *buf, size_t buf_len, const char *data, size_t data_len); +int dbg_dec_hex(const char *buf, size_t buf_len, char *data, size_t data_len); +int dbg_enc_bin(char *buf, size_t buf_len, const char *data, size_t data_len); +int dbg_dec_bin(const char *buf, size_t buf_len, char *data, size_t data_len); + +/* Packet creation helpers */ +int dbg_send_ok_packet(char *buf, size_t buf_len); +int dbg_send_conmsg_packet(char *buf, size_t buf_len, const char *msg); +int dbg_send_signal_packet(char *buf, size_t buf_len, char signal); +int dbg_send_error_packet(char *buf, size_t buf_len, char error); + +/* Command functions */ +int dbg_mem_read(char *buf, size_t buf_len, address addr, size_t len, dbg_enc_func enc); +int dbg_mem_write(const char *buf, size_t buf_len, address addr, size_t len, dbg_dec_func dec); +int dbg_continue(void); +int dbg_step(void); + +/***************************************************************************** + * String Processing Helper Functions + ****************************************************************************/ + +/* + * Get null-terminated string length. + */ +int dbg_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. + */ +int dbg_strtol(const char *str, size_t len, int base, const char **endptr) +{ + size_t pos; + int sign, tmp, value, valid; + + value = 0; + pos = 0; + sign = 1; + valid = 0; + + if (endptr) { + *endptr = NULL; + } + + 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 = dbg_get_val(str[pos], base); + if (tmp == 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. + */ +char dbg_get_digit(int val) +{ + if ((val >= 0) && (val <= 0xf)) { + return digits[val]; + } else { + return EOF; + } +} + +/* + * Get the corresponding value for a ASCII digit character. + * + * Supports bases 2-16. + */ +int dbg_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 EOF; + } + + return (value < base) ? value : EOF; +} + +/* + * Determine if this is a printable ASCII character. + */ +int dbg_is_printable_char(char ch) +{ + return (ch >= 0x20 && ch <= 0x7e); +} + +/***************************************************************************** + * Packet Functions + ****************************************************************************/ + +/* + * Receive a packet acknowledgment + * + * Returns: + * 0 if an ACK (+) was received + * 1 if a NACK (-) was received + * EOF otherwise + */ +int dbg_recv_ack(void) +{ + int response; + + /* Wait for packet ack */ + switch (response = dbg_sys_getc()) { + case '+': + /* Packet acknowledged */ + return 0; + case '-': + /* Packet negative acknowledged */ + return 1; + default: + /* Bad response! */ + DEBUG_PRINT("received bad packet response: 0x%2x\n", response); + return EOF; + } +} + +/* + * Calculate 8-bit checksum of a buffer. + * + * Returns: + * 8-bit checksum. + */ +int dbg_checksum(const char *buf, size_t 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 + * EOF otherwise + */ +int dbg_send_packet(const char *pkt_data, size_t pkt_len) +{ + char buf[3]; + char csum; + + /* Send packet start */ + if (dbg_sys_putchar('$') == EOF) { + return EOF; + } + +#if DEBUG + { + size_t p; + DEBUG_PRINT("-> "); + for (p = 0; p < pkt_len; p++) { + if (dbg_is_printable_char(pkt_data[p])) { + DEBUG_PRINT("%c", pkt_data[p]); + } else { + DEBUG_PRINT("\\x%02x", pkt_data[p]&0xff); + } + } + DEBUG_PRINT("\n"); + } +#endif + + /* Send packet data */ + if (dbg_write(pkt_data, pkt_len) == EOF) { + return EOF; + } + + /* Send the checksum */ + buf[0] = '#'; + csum = dbg_checksum(pkt_data, pkt_len); + if ((dbg_enc_hex(buf+1, sizeof(buf)-1, &csum, 1) == EOF) || + (dbg_write(buf, sizeof(buf)) == EOF)) { + return EOF; + } + + return dbg_recv_ack(); +} + +/* + * Receives a packet of data, assuming a 7-bit clean connection. + * + * Returns: + * 0 if the packet was received + * EOF otherwise + */ +int dbg_recv_packet(char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len) +{ + int data; + char expected_csum, actual_csum; + char buf[2]; + + /* Wait for packet start */ + actual_csum = 0; + + while (1) { + data = dbg_sys_getc(); + if (data == '$') { + /* Detected start of packet. */ + break; + } + } + + /* Read until checksum */ + *pkt_len = 0; + while (1) { + data = dbg_sys_getc(); + + if (data == EOF) { + /* Error receiving character */ + return EOF; + } else if (data == '#') { + /* End of packet */ + break; + } else { + /* Check for space */ + if (*pkt_len >= pkt_buf_len) { + DEBUG_PRINT("packet buffer overflow\n"); + return EOF; + } + + /* Store character and update checksum */ + pkt_buf[(*pkt_len)++] = (char) data; + } + } + +#if DEBUG + { + size_t p; + DEBUG_PRINT("<- "); + for (p = 0; p < *pkt_len; p++) { + if (dbg_is_printable_char(pkt_buf[p])) { + DEBUG_PRINT("%c", pkt_buf[p]); + } else { + DEBUG_PRINT("\\x%02x", pkt_buf[p] & 0xff); + } + } + DEBUG_PRINT("\n"); + } +#endif + + /* Receive the checksum */ + if ((dbg_read(buf, sizeof(buf), 2) == EOF) || + (dbg_dec_hex(buf, 2, &expected_csum, 1) == EOF)) { + return EOF; + } + + /* Verify checksum */ + actual_csum = dbg_checksum(pkt_buf, *pkt_len); + if (actual_csum != expected_csum) { + /* Send packet nack */ + DEBUG_PRINT("received packet with bad checksum\n"); + dbg_sys_putchar('-'); + return EOF; + } + + /* Send packet ack */ + dbg_sys_putchar('+'); + return 0; +} + +/***************************************************************************** + * Data Encoding/Decoding + ****************************************************************************/ + +/* + * Encode data to its hex-value representation in a buffer. + * + * Returns: + * 0+ number of bytes written to buf + * EOF if the buffer is too small + */ +int dbg_enc_hex(char *buf, size_t buf_len, const char *data, size_t data_len) +{ + size_t pos; + + if (buf_len < data_len*2) { + /* Buffer too small */ + return EOF; + } + + for (pos = 0; pos < data_len; pos++) { + *buf++ = dbg_get_digit((data[pos] >> 4) & 0xf); + *buf++ = dbg_get_digit((data[pos] ) & 0xf); + } + + return data_len*2; +} + +/* + * Decode data from its hex-value representation to a buffer. + * + * Returns: + * 0 if successful + * EOF if the buffer is too small + */ +int dbg_dec_hex(const char *buf, size_t buf_len, char *data, size_t data_len) +{ + size_t pos; + int tmp; + + if (buf_len != data_len*2) { + /* Buffer too small */ + return EOF; + } + + for (pos = 0; pos < data_len; pos++) { + /* Decode high nibble */ + tmp = dbg_get_val(*buf++, 16); + if (tmp == EOF) { + /* Buffer contained junk. */ + ASSERT(0); + return EOF; + } + + data[pos] = tmp << 4; + + /* Decode low nibble */ + tmp = dbg_get_val(*buf++, 16); + if (tmp == EOF) { + /* Buffer contained junk. */ + ASSERT(0); + return EOF; + } + data[pos] |= tmp; + } + + return 0; +} + +/* + * Encode data to its binary representation in a buffer. + * + * Returns: + * 0+ number of bytes written to buf + * EOF if the buffer is too small + */ +int dbg_enc_bin(char *buf, size_t buf_len, const char *data, size_t data_len) +{ + size_t 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) { + ASSERT(0); + return EOF; + } + buf[buf_pos++] = '}'; + buf[buf_pos++] = data[data_pos] ^ 0x20; + } else { + if (buf_pos >= buf_len) { + ASSERT(0); + return 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 + * EOF if the buffer is too small + */ +int dbg_dec_bin(const char *buf, size_t buf_len, char *data, size_t data_len) +{ + size_t 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 */ + ASSERT(0); + return 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. */ + ASSERT(0); + return 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 + * EOF if the buffer is too small + */ +int dbg_mem_read(char *buf, size_t buf_len, address addr, size_t len, dbg_enc_func enc) +{ + char data[64]; + size_t pos; + + if (len > sizeof(data)) { + return EOF; + } + + /* Read from system memory */ + for (pos = 0; pos < len; pos++) { + if (dbg_sys_mem_readb(addr+pos, &data[pos])) { + /* Failed to read */ + return EOF; + } + } + + /* Encode data */ + return enc(buf, buf_len, data, len); +} + +/* + * Write to memory from encoded buf. + */ +int dbg_mem_write(const char *buf, size_t buf_len, address addr, size_t len, dbg_dec_func dec) +{ + char data[64]; + size_t pos; + + if (len > sizeof(data)) { + return EOF; + } + + /* Decode data */ + if (dec(buf, buf_len, data, len) == EOF) { + return EOF; + } + + /* Write to system memory */ + for (pos = 0; pos < len; pos++) { + if (dbg_sys_mem_writeb(addr+pos, data[pos])) { + /* Failed to write */ + return EOF; + } + } + + return 0; +} + +/* + * Continue program execution at PC. + */ +int dbg_continue(void) +{ + dbg_sys_continue(); + return 0; +} + +/* + * Step one instruction. + */ +int dbg_step(void) +{ + dbg_sys_step(); + return 0; +} + +/***************************************************************************** + * Packet Creation Helpers + ****************************************************************************/ + +/* + * Send OK packet + */ +int dbg_send_ok_packet(char *buf, size_t buf_len) +{ + return dbg_send_packet("OK", 2); +} + +/* + * Send a message to the debugging console (via O XX... packet) + */ +int dbg_send_conmsg_packet(char *buf, size_t buf_len, const char *msg) +{ + size_t size; + int status; + + if (buf_len < 2) { + /* Buffer too small */ + return EOF; + } + + buf[0] = 'O'; + status = dbg_enc_hex(&buf[1], buf_len-1, msg, dbg_strlen(msg)); + if (status == EOF) { + return EOF; + } + size = 1 + status; + return dbg_send_packet(buf, size); +} + +/* + * Send a signal packet (S AA). + */ +int dbg_send_signal_packet(char *buf, size_t buf_len, char signal) +{ + size_t size; + int status; + + if (buf_len < 4) { + /* Buffer too small */ + return EOF; + } + + buf[0] = 'S'; + status = dbg_enc_hex(&buf[1], buf_len-1, &signal, 1); + if (status == EOF) { + return EOF; + } + size = 1 + status; + return dbg_send_packet(buf, size); +} + +/* + * Send a error packet (E AA). + */ +int dbg_send_error_packet(char *buf, size_t buf_len, char error) +{ + size_t size; + int status; + + if (buf_len < 4) { + /* Buffer too small */ + return EOF; + } + + buf[0] = 'E'; + status = dbg_enc_hex(&buf[1], buf_len-1, &error, 1); + if (status == EOF) { + return EOF; + } + size = 1 + status; + return dbg_send_packet(buf, size); +} + +/***************************************************************************** + * Communication Functions + ****************************************************************************/ + +/* + * Write a sequence of bytes. + * + * Returns: + * 0 if successful + * EOF if failed to write all bytes + */ +int dbg_write(const char *buf, size_t len) +{ + while (len--) { + if (dbg_sys_putchar(*buf++) == EOF) { + return EOF; + } + } + + return 0; +} + +/* + * Read a sequence of bytes. + * + * Returns: + * 0 if successfully read len bytes + * EOF if failed to read all bytes + */ +int dbg_read(char *buf, size_t buf_len, size_t len) +{ + char c; + + if (buf_len < len) { + /* Buffer too small */ + return EOF; + } + + while (len--) { + if ((c = dbg_sys_getc()) == EOF) { + return EOF; + } + *buf++ = c; + } + + return 0; +} + +/***************************************************************************** + * Main Loop + ****************************************************************************/ + +/* + * Main debug loop. Handles commands. + */ +int dbg_main(struct dbg_state *state) +{ + address addr; + char pkt_buf[256]; + int status; + size_t length; + size_t pkt_len; + const char *ptr_next; + + dbg_send_signal_packet(pkt_buf, sizeof(pkt_buf), state->signum); + + while (1) { + /* Receive the next packet */ + status = dbg_recv_packet(pkt_buf, sizeof(pkt_buf), &pkt_len); + if (status == 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 = dbg_strtol(ptr_next, token_remaining_buf, \ + 16, &ptr_next); \ + if (!ptr_next) { \ + goto error; \ + } \ + } + + /* + * Read Registers + * Command Format: g + */ + case 'g': + /* Encode registers */ + status = dbg_enc_hex(pkt_buf, sizeof(pkt_buf), + (char *)&(state->registers), + sizeof(state->registers)); + if (status == EOF) { + goto error; + } + pkt_len = status; + dbg_send_packet(pkt_buf, pkt_len); + break; + + /* + * Write Registers + * Command Format: G XX... + */ + case 'G': + status = dbg_dec_hex(pkt_buf+1, pkt_len-1, + (char *)&(state->registers), + sizeof(state->registers)); + if (status == EOF) { + goto error; + } + dbg_send_ok_packet(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 >= DBG_CPU_I386_NUM_REGISTERS) { + goto error; + } + + /* Read Register */ + status = dbg_enc_hex(pkt_buf, sizeof(pkt_buf), + (char *)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == EOF) { + goto error; + } + dbg_send_packet(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 < DBG_CPU_I386_NUM_REGISTERS) { + status = dbg_dec_hex(ptr_next, token_remaining_buf, + (char *)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == EOF) { + goto error; + } + } + dbg_send_ok_packet(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 = dbg_mem_read(pkt_buf, sizeof(pkt_buf), + addr, length, dbg_enc_hex); + if (status == EOF) { + goto error; + } + dbg_send_packet(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 = dbg_mem_write(ptr_next, token_remaining_buf, + addr, length, dbg_dec_hex); + if (status == EOF) { + goto error; + } + dbg_send_ok_packet(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 = dbg_mem_write(ptr_next, token_remaining_buf, + addr, length, dbg_dec_bin); + if (status == EOF) { + goto error; + } + dbg_send_ok_packet(pkt_buf, sizeof(pkt_buf)); + break; + + /* + * Continue + * Command Format: c [addr] + */ + case 'c': + dbg_continue(); + return 0; + + /* + * Single-step + * Command Format: s [addr] + */ + case 's': + dbg_step(); + return 0; + + case '?': + dbg_send_signal_packet(pkt_buf, sizeof(pkt_buf), state->signum); + break; + + /* + * Unsupported Command + */ + default: + dbg_send_packet(NULL, 0); + } + + continue; + + error: + dbg_send_error_packet(pkt_buf, sizeof(pkt_buf), 0x00); + + #undef token_remaining_buf + #undef token_expect_seperator + #undef token_expect_integer_arg + } + + return 0; +} diff --git a/gdbstub_rsp.c b/gdbstub_rsp.c deleted file mode 100644 index 439bd4f..0000000 --- a/gdbstub_rsp.c +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright (c) 2016-2019 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 "gdbstub.h" - -/***************************************************************************** - * Types - ****************************************************************************/ - -typedef int (*dbg_enc_func)(char *buf, size_t buf_len, const char *data, size_t data_len); -typedef int (*dbg_dec_func)(const char *buf, size_t buf_len, char *data, size_t data_len); - -/***************************************************************************** - * Const Data - ****************************************************************************/ - -const char digits[] = "0123456789abcdef"; - -/***************************************************************************** - * Prototypes - ****************************************************************************/ - -/* Communication functions */ -int dbg_write(const char *buf, size_t len); -int dbg_read(char *buf, size_t buf_len, size_t len); - -/* String processing helper functions */ -int dbg_strlen(const char *ch); -int dbg_is_printable_char(char ch); -char dbg_get_digit(int val); -int dbg_get_val(char digit, int base); -int dbg_strtol(const char *str, size_t len, int base, const char **endptr); - -/* Packet functions */ -int dbg_send_packet(const char *pkt, size_t pkt_len); -int dbg_recv_packet(char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len); -int dbg_checksum(const char *buf, size_t len); -int dbg_recv_ack(void); - -/* Data encoding/decoding */ -int dbg_enc_hex(char *buf, size_t buf_len, const char *data, size_t data_len); -int dbg_dec_hex(const char *buf, size_t buf_len, char *data, size_t data_len); -int dbg_enc_bin(char *buf, size_t buf_len, const char *data, size_t data_len); -int dbg_dec_bin(const char *buf, size_t buf_len, char *data, size_t data_len); - -/* Packet creation helpers */ -int dbg_send_ok_packet(char *buf, size_t buf_len); -int dbg_send_conmsg_packet(char *buf, size_t buf_len, const char *msg); -int dbg_send_signal_packet(char *buf, size_t buf_len, char signal); -int dbg_send_error_packet(char *buf, size_t buf_len, char error); - -/* Command functions */ -int dbg_mem_read(char *buf, size_t buf_len, address addr, size_t len, dbg_enc_func enc); -int dbg_mem_write(const char *buf, size_t buf_len, address addr, size_t len, dbg_dec_func dec); -int dbg_continue(void); -int dbg_step(void); - -/***************************************************************************** - * String Processing Helper Functions - ****************************************************************************/ - -/* - * Get null-terminated string length. - */ -int dbg_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. - */ -int dbg_strtol(const char *str, size_t len, int base, const char **endptr) -{ - size_t pos; - int sign, tmp, value, valid; - - value = 0; - pos = 0; - sign = 1; - valid = 0; - - if (endptr) { - *endptr = NULL; - } - - 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 = dbg_get_val(str[pos], base); - if (tmp == 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. - */ -char dbg_get_digit(int val) -{ - if ((val >= 0) && (val <= 0xf)) { - return digits[val]; - } else { - return EOF; - } -} - -/* - * Get the corresponding value for a ASCII digit character. - * - * Supports bases 2-16. - */ -int dbg_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 EOF; - } - - return (value < base) ? value : EOF; -} - -/* - * Determine if this is a printable ASCII character. - */ -int dbg_is_printable_char(char ch) -{ - return (ch >= 0x20 && ch <= 0x7e); -} - -/***************************************************************************** - * Packet Functions - ****************************************************************************/ - -/* - * Receive a packet acknowledgment - * - * Returns: - * 0 if an ACK (+) was received - * 1 if a NACK (-) was received - * EOF otherwise - */ -int dbg_recv_ack(void) -{ - int response; - - /* Wait for packet ack */ - switch (response = dbg_sys_getc()) { - case '+': - /* Packet acknowledged */ - return 0; - case '-': - /* Packet negative acknowledged */ - return 1; - default: - /* Bad response! */ - DEBUG_PRINT("received bad packet response: 0x%2x\n", response); - return EOF; - } -} - -/* - * Calculate 8-bit checksum of a buffer. - * - * Returns: - * 8-bit checksum. - */ -int dbg_checksum(const char *buf, size_t 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 - * EOF otherwise - */ -int dbg_send_packet(const char *pkt_data, size_t pkt_len) -{ - char buf[3]; - char csum; - - /* Send packet start */ - if (dbg_sys_putchar('$') == EOF) { - return EOF; - } - -#if DEBUG - { - size_t p; - DEBUG_PRINT("-> "); - for (p = 0; p < pkt_len; p++) { - if (dbg_is_printable_char(pkt_data[p])) { - DEBUG_PRINT("%c", pkt_data[p]); - } else { - DEBUG_PRINT("\\x%02x", pkt_data[p]&0xff); - } - } - DEBUG_PRINT("\n"); - } -#endif - - /* Send packet data */ - if (dbg_write(pkt_data, pkt_len) == EOF) { - return EOF; - } - - /* Send the checksum */ - buf[0] = '#'; - csum = dbg_checksum(pkt_data, pkt_len); - if ((dbg_enc_hex(buf+1, sizeof(buf)-1, &csum, 1) == EOF) || - (dbg_write(buf, sizeof(buf)) == EOF)) { - return EOF; - } - - return dbg_recv_ack(); -} - -/* - * Receives a packet of data, assuming a 7-bit clean connection. - * - * Returns: - * 0 if the packet was received - * EOF otherwise - */ -int dbg_recv_packet(char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len) -{ - int data; - char expected_csum, actual_csum; - char buf[2]; - - /* Wait for packet start */ - actual_csum = 0; - - while (1) { - data = dbg_sys_getc(); - if (data == '$') { - /* Detected start of packet. */ - break; - } - } - - /* Read until checksum */ - *pkt_len = 0; - while (1) { - data = dbg_sys_getc(); - - if (data == EOF) { - /* Error receiving character */ - return EOF; - } else if (data == '#') { - /* End of packet */ - break; - } else { - /* Check for space */ - if (*pkt_len >= pkt_buf_len) { - DEBUG_PRINT("packet buffer overflow\n"); - return EOF; - } - - /* Store character and update checksum */ - pkt_buf[(*pkt_len)++] = (char) data; - } - } - -#if DEBUG - { - size_t p; - DEBUG_PRINT("<- "); - for (p = 0; p < *pkt_len; p++) { - if (dbg_is_printable_char(pkt_buf[p])) { - DEBUG_PRINT("%c", pkt_buf[p]); - } else { - DEBUG_PRINT("\\x%02x", pkt_buf[p] & 0xff); - } - } - DEBUG_PRINT("\n"); - } -#endif - - /* Receive the checksum */ - if ((dbg_read(buf, sizeof(buf), 2) == EOF) || - (dbg_dec_hex(buf, 2, &expected_csum, 1) == EOF)) { - return EOF; - } - - /* Verify checksum */ - actual_csum = dbg_checksum(pkt_buf, *pkt_len); - if (actual_csum != expected_csum) { - /* Send packet nack */ - DEBUG_PRINT("received packet with bad checksum\n"); - dbg_sys_putchar('-'); - return EOF; - } - - /* Send packet ack */ - dbg_sys_putchar('+'); - return 0; -} - -/***************************************************************************** - * Data Encoding/Decoding - ****************************************************************************/ - -/* - * Encode data to its hex-value representation in a buffer. - * - * Returns: - * 0+ number of bytes written to buf - * EOF if the buffer is too small - */ -int dbg_enc_hex(char *buf, size_t buf_len, const char *data, size_t data_len) -{ - size_t pos; - - if (buf_len < data_len*2) { - /* Buffer too small */ - return EOF; - } - - for (pos = 0; pos < data_len; pos++) { - *buf++ = dbg_get_digit((data[pos] >> 4) & 0xf); - *buf++ = dbg_get_digit((data[pos] ) & 0xf); - } - - return data_len*2; -} - -/* - * Decode data from its hex-value representation to a buffer. - * - * Returns: - * 0 if successful - * EOF if the buffer is too small - */ -int dbg_dec_hex(const char *buf, size_t buf_len, char *data, size_t data_len) -{ - size_t pos; - int tmp; - - if (buf_len != data_len*2) { - /* Buffer too small */ - return EOF; - } - - for (pos = 0; pos < data_len; pos++) { - /* Decode high nibble */ - tmp = dbg_get_val(*buf++, 16); - if (tmp == EOF) { - /* Buffer contained junk. */ - ASSERT(0); - return EOF; - } - - data[pos] = tmp << 4; - - /* Decode low nibble */ - tmp = dbg_get_val(*buf++, 16); - if (tmp == EOF) { - /* Buffer contained junk. */ - ASSERT(0); - return EOF; - } - data[pos] |= tmp; - } - - return 0; -} - -/* - * Encode data to its binary representation in a buffer. - * - * Returns: - * 0+ number of bytes written to buf - * EOF if the buffer is too small - */ -int dbg_enc_bin(char *buf, size_t buf_len, const char *data, size_t data_len) -{ - size_t 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) { - ASSERT(0); - return EOF; - } - buf[buf_pos++] = '}'; - buf[buf_pos++] = data[data_pos] ^ 0x20; - } else { - if (buf_pos >= buf_len) { - ASSERT(0); - return 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 - * EOF if the buffer is too small - */ -int dbg_dec_bin(const char *buf, size_t buf_len, char *data, size_t data_len) -{ - size_t 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 */ - ASSERT(0); - return 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. */ - ASSERT(0); - return 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 - * EOF if the buffer is too small - */ -int dbg_mem_read(char *buf, size_t buf_len, address addr, size_t len, dbg_enc_func enc) -{ - char data[64]; - size_t pos; - - if (len > sizeof(data)) { - return EOF; - } - - /* Read from system memory */ - for (pos = 0; pos < len; pos++) { - if (dbg_sys_mem_readb(addr+pos, &data[pos])) { - /* Failed to read */ - return EOF; - } - } - - /* Encode data */ - return enc(buf, buf_len, data, len); -} - -/* - * Write to memory from encoded buf. - */ -int dbg_mem_write(const char *buf, size_t buf_len, address addr, size_t len, dbg_dec_func dec) -{ - char data[64]; - size_t pos; - - if (len > sizeof(data)) { - return EOF; - } - - /* Decode data */ - if (dec(buf, buf_len, data, len) == EOF) { - return EOF; - } - - /* Write to system memory */ - for (pos = 0; pos < len; pos++) { - if (dbg_sys_mem_writeb(addr+pos, data[pos])) { - /* Failed to write */ - return EOF; - } - } - - return 0; -} - -/* - * Continue program execution at PC. - */ -int dbg_continue(void) -{ - dbg_sys_continue(); - return 0; -} - -/* - * Step one instruction. - */ -int dbg_step(void) -{ - dbg_sys_step(); - return 0; -} - -/***************************************************************************** - * Packet Creation Helpers - ****************************************************************************/ - -/* - * Send OK packet - */ -int dbg_send_ok_packet(char *buf, size_t buf_len) -{ - return dbg_send_packet("OK", 2); -} - -/* - * Send a message to the debugging console (via O XX... packet) - */ -int dbg_send_conmsg_packet(char *buf, size_t buf_len, const char *msg) -{ - size_t size; - int status; - - if (buf_len < 2) { - /* Buffer too small */ - return EOF; - } - - buf[0] = 'O'; - status = dbg_enc_hex(&buf[1], buf_len-1, msg, dbg_strlen(msg)); - if (status == EOF) { - return EOF; - } - size = 1 + status; - return dbg_send_packet(buf, size); -} - -/* - * Send a signal packet (S AA). - */ -int dbg_send_signal_packet(char *buf, size_t buf_len, char signal) -{ - size_t size; - int status; - - if (buf_len < 4) { - /* Buffer too small */ - return EOF; - } - - buf[0] = 'S'; - status = dbg_enc_hex(&buf[1], buf_len-1, &signal, 1); - if (status == EOF) { - return EOF; - } - size = 1 + status; - return dbg_send_packet(buf, size); -} - -/* - * Send a error packet (E AA). - */ -int dbg_send_error_packet(char *buf, size_t buf_len, char error) -{ - size_t size; - int status; - - if (buf_len < 4) { - /* Buffer too small */ - return EOF; - } - - buf[0] = 'E'; - status = dbg_enc_hex(&buf[1], buf_len-1, &error, 1); - if (status == EOF) { - return EOF; - } - size = 1 + status; - return dbg_send_packet(buf, size); -} - -/***************************************************************************** - * Communication Functions - ****************************************************************************/ - -/* - * Write a sequence of bytes. - * - * Returns: - * 0 if successful - * EOF if failed to write all bytes - */ -int dbg_write(const char *buf, size_t len) -{ - while (len--) { - if (dbg_sys_putchar(*buf++) == EOF) { - return EOF; - } - } - - return 0; -} - -/* - * Read a sequence of bytes. - * - * Returns: - * 0 if successfully read len bytes - * EOF if failed to read all bytes - */ -int dbg_read(char *buf, size_t buf_len, size_t len) -{ - char c; - - if (buf_len < len) { - /* Buffer too small */ - return EOF; - } - - while (len--) { - if ((c = dbg_sys_getc()) == EOF) { - return EOF; - } - *buf++ = c; - } - - return 0; -} - -/***************************************************************************** - * Main Loop - ****************************************************************************/ - -/* - * Main debug loop. Handles commands. - */ -int dbg_main(struct dbg_state *state) -{ - address addr; - char pkt_buf[256]; - int status; - size_t length; - size_t pkt_len; - const char *ptr_next; - - dbg_send_signal_packet(pkt_buf, sizeof(pkt_buf), state->signum); - - while (1) { - /* Receive the next packet */ - status = dbg_recv_packet(pkt_buf, sizeof(pkt_buf), &pkt_len); - if (status == 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 = dbg_strtol(ptr_next, token_remaining_buf, \ - 16, &ptr_next); \ - if (!ptr_next) { \ - goto error; \ - } \ - } - - /* - * Read Registers - * Command Format: g - */ - case 'g': - /* Encode registers */ - status = dbg_enc_hex(pkt_buf, sizeof(pkt_buf), - (char *)&(state->registers), - sizeof(state->registers)); - if (status == EOF) { - goto error; - } - pkt_len = status; - dbg_send_packet(pkt_buf, pkt_len); - break; - - /* - * Write Registers - * Command Format: G XX... - */ - case 'G': - status = dbg_dec_hex(pkt_buf+1, pkt_len-1, - (char *)&(state->registers), - sizeof(state->registers)); - if (status == EOF) { - goto error; - } - dbg_send_ok_packet(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 >= DBG_CPU_I386_NUM_REGISTERS) { - goto error; - } - - /* Read Register */ - status = dbg_enc_hex(pkt_buf, sizeof(pkt_buf), - (char *)&(state->registers[addr]), - sizeof(state->registers[addr])); - if (status == EOF) { - goto error; - } - dbg_send_packet(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 < DBG_CPU_I386_NUM_REGISTERS) { - status = dbg_dec_hex(ptr_next, token_remaining_buf, - (char *)&(state->registers[addr]), - sizeof(state->registers[addr])); - if (status == EOF) { - goto error; - } - } - dbg_send_ok_packet(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 = dbg_mem_read(pkt_buf, sizeof(pkt_buf), - addr, length, dbg_enc_hex); - if (status == EOF) { - goto error; - } - dbg_send_packet(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 = dbg_mem_write(ptr_next, token_remaining_buf, - addr, length, dbg_dec_hex); - if (status == EOF) { - goto error; - } - dbg_send_ok_packet(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 = dbg_mem_write(ptr_next, token_remaining_buf, - addr, length, dbg_dec_bin); - if (status == EOF) { - goto error; - } - dbg_send_ok_packet(pkt_buf, sizeof(pkt_buf)); - break; - - /* - * Continue - * Command Format: c [addr] - */ - case 'c': - dbg_continue(); - return 0; - - /* - * Single-step - * Command Format: s [addr] - */ - case 's': - dbg_step(); - return 0; - - case '?': - dbg_send_signal_packet(pkt_buf, sizeof(pkt_buf), state->signum); - break; - - /* - * Unsupported Command - */ - default: - dbg_send_packet(NULL, 0); - } - - continue; - - error: - dbg_send_error_packet(pkt_buf, sizeof(pkt_buf), 0x00); - - #undef token_remaining_buf - #undef token_expect_seperator - #undef token_expect_integer_arg - } - - return 0; -} -- cgit v1.2.3