diff --git a/.gitignore b/.gitignore index 0bb6e87397de23e9e7eae63c49bd273cfec03211..27f3b4daad5fafe414f6f7d60d351f54f77d9708 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ src/platform/Kconfig src/include/nx_configure.h platform.mk rootfs.cpio -rootfs.img \ No newline at end of file +rootfs.img +build \ No newline at end of file diff --git a/src/arch/riscv64/include/gdbstub.h b/src/arch/riscv64/include/gdbstub.h new file mode 100644 index 0000000000000000000000000000000000000000..d4e4d03a7ee44c8120f28ebb0ac51da7c92a41a8 --- /dev/null +++ b/src/arch/riscv64/include/gdbstub.h @@ -0,0 +1,12 @@ + + +#ifndef RISCV_GDB_STUB +#define RISCV_GDB_STUB + + +#include +#include + +void GdbStubTrapHandler(NX_HalTrapFrame *frame); + +#endif \ No newline at end of file diff --git a/src/arch/riscv64/include/ptrace.h b/src/arch/riscv64/include/ptrace.h new file mode 100644 index 0000000000000000000000000000000000000000..a94b2d31ee898c9bb5b5c3fbd0031ef68a285588 --- /dev/null +++ b/src/arch/riscv64/include/ptrace.h @@ -0,0 +1,24 @@ +#ifndef __PTRACE_RISCV_H__ +#define __PTRACE_RISCV_H__ +#include "base/error.h" +#include "base/ptrace.h" +#include "base/thread.h" +#include "irq.h" + +struct Registers{ + NX_UArch reg[32]; +}NX_PACKED; +typedef struct Registers Registers; + +struct FpRegisters{ + NX_UArch fp[32]; +}NX_PACKED; +typedef struct FpRegisters FpRegisters; + +#define gdb_c_ebreak_instruction 0x9002 +#define gdb_ebreak_instruction 0x00100073 + +NX_Error Ptrace(enum __ptrace_request request, NX_Thread* target, void * addr, void * data); +NX_I32 SingleStepCallBack(NX_Thread * thread, NX_HalTrapFrame * trapFrame); +NX_I32 StartSingleStep(NX_Thread * thread); +#endif \ No newline at end of file diff --git a/src/arch/riscv64/kernel/gdbstub.c b/src/arch/riscv64/kernel/gdbstub.c new file mode 100644 index 0000000000000000000000000000000000000000..bbc3539f80c22b223a253f30b86109df28410a4d --- /dev/null +++ b/src/arch/riscv64/kernel/gdbstub.c @@ -0,0 +1,1786 @@ +#include +#include +#include +#include +#include + + +#define GDB_EOF -1 +#define GDB_PRINT(...) +#define GDB_ASSERT(x) \ + do { \ + } while (0) +#define PKT_BUF_LEN 4096 + +#ifndef __GDB_MAX_BREAKPOINTS_NUM_ +#define __GDB_MAX_BREAKPOINTS_NUM_ 100 +#endif + +#ifndef __GDB_MAX_WATCHPOINTS_NUM_ +#define __GDB_MAX_WATCHPOINTS_NUM_ 100 +#endif + +#define insert_break_or_watchpoint_software_breakpoint 0 +#define insert_break_or_watchpoint_hardware_breakpoint 1 +#define insert_break_or_watchpoint_write_watchpoint 2 +#define insert_break_or_watchpoint_read_watchpoint 3 +#define insert_break_or_watchpoint_access_watchpoint 4 + +#define gdb_c_ebreak_instruction 0x9002 +#define gdb_ebreak_instruction 0x00100073 + +typedef NX_U64 reg; +typedef NX_Addr address; + +enum GDB_REGISTER { + GDB_CPU_RISCV_REG_X0 = 0, + GDB_CPU_RISCV_REG_RA = 1, + GDB_CPU_RISCV_REG_SP = 2, + GDB_CPU_RISCV_REG_GP = 3, + GDB_CPU_RISCV_REG_TP = 4, + GDB_CPU_RISCV_REG_T0 = 5, + GDB_CPU_RISCV_REG_T1 = 6, + GDB_CPU_RISCV_REG_T2 = 7, + GDB_CPU_RISCV_REG_S0 = 8, + GDB_CPU_RISCV_REG_S1 = 9, + GDB_CPU_RISCV_REG_A0 = 10, + GDB_CPU_RISCV_REG_A1 = 11, + GDB_CPU_RISCV_REG_A2 = 12, + GDB_CPU_RISCV_REG_A3 = 13, + GDB_CPU_RISCV_REG_A4 = 14, + GDB_CPU_RISCV_REG_A5 = 15, + GDB_CPU_RISCV_REG_A6 = 16, + GDB_CPU_RISCV_REG_A7 = 17, + GDB_CPU_RISCV_REG_S2 = 18, + GDB_CPU_RISCV_REG_S3 = 19, + GDB_CPU_RISCV_REG_S4 = 20, + GDB_CPU_RISCV_REG_S5 = 21, + GDB_CPU_RISCV_REG_S6 = 22, + GDB_CPU_RISCV_REG_S7 = 23, + GDB_CPU_RISCV_REG_S8 = 24, + GDB_CPU_RISCV_REG_S9 = 25, + GDB_CPU_RISCV_REG_S10 = 26, + GDB_CPU_RISCV_REG_S11 = 27, + GDB_CPU_RISCV_REG_T3 = 28, + GDB_CPU_RISCV_REG_T4 = 29, + GDB_CPU_RISCV_REG_T5 = 30, + GDB_CPU_RISCV_REG_T6 = 31, + GDB_CPU_RISCV_REG_PC = 32, + GDB_CPU_NUM_REGISTERS = 33 +}; + +typedef struct GdbState { + int signum; + // lastbp >=0 means the last 'step' command's instruction is a breakpoint + // lastbp == -1 means the last 'step' command's instruction is not a + // breakpoint lastbp == -2 means there is no step command + int lastbp; + NX_U32 tmp_instruction; + reg registers[GDB_CPU_NUM_REGISTERS]; +} GdbState; + +typedef struct { + volatile NX_U64 en; + volatile NX_U8 type; + volatile NX_U64 addr; + volatile NX_U64 len; + volatile NX_U32 instruction; +} gdb_bkp_or_watch_t; + + +void gdb_sys_init(void); +static int gdb_sys_getc(struct GdbState *state); +static int gdb_sys_putchar(struct GdbState *state, int ch); +static int gdb_sys_mem_readb(struct GdbState *state, address addr, char *val); +static int gdb_sys_mem_writeb(struct GdbState *state, address addr, char val); + +/***************************************************************************** + * 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 + ****************************************************************************/ + +const char digits[17] = "0123456789abcdef"; + +/***************************************************************************** + * Prototypes + ****************************************************************************/ + +/* Communication functions */ +static int gdb_write(struct GdbState *state, const char *buf, + unsigned int len); +static int gdb_read(struct GdbState *state, char *buf, unsigned int buf_len, + unsigned int len); + +/* String processing helper functions */ +static int gdb_strlen(const char *ch); +#if DEBUG +static int gdb_is_printable_char(char ch); +#endif +static char gdb_get_digit(int val); +static int gdb_get_val(char digit, int base); +static int gdb_strtoi(const char *str, unsigned int len, int base, + const char **endptr); +static long gdb_strtol(const char *str, unsigned int len, int base, + const char **endptr); + +/* Packet functions */ +static int gdb_send_packet(struct GdbState *state, const char *pkt, + unsigned int pkt_len); +static int gdb_recv_packet(struct GdbState *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 GdbState *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 GdbState *state, char *buf, + unsigned int buf_len); +static int gdb_send_conmsg_packet(struct GdbState *state, char *buf, + unsigned int buf_len, const char *msg); +static int gdb_send_signal_packet(struct GdbState *state, char *buf, + unsigned int buf_len, char signal); +static int gdb_send_error_packet(struct GdbState *state, char *buf, + unsigned int buf_len, char error); + +/* Command functions */ +static int gdb_mem_read(struct GdbState *state, char *buf, + unsigned int buf_len, address addr, unsigned int len, + gdb_enc_func enc); +static int gdb_mem_write(struct GdbState *state, const char *buf, + unsigned int buf_len, address addr, unsigned int len, + gdb_dec_func dec); +static int gdb_continue(struct GdbState *state); +static int gdb_step(struct GdbState *state); + + + +static GdbState gdbStateGlobal; +static int isStartEbreakExectued; +static gdb_bkp_or_watch_t gdb_bkp_buf[__GDB_MAX_BREAKPOINTS_NUM_]; +static gdb_bkp_or_watch_t gdb_watch_buf[__GDB_MAX_WATCHPOINTS_NUM_]; + +/* + * Write one character to the debugging stream. + */ +static int gdb_sys_putchar(struct GdbState *state, int ch) { + // tode + return ch; +} + +/* + * Read one character from the debugging stream. + */ +static int gdb_sys_getc(struct GdbState *state) { + // todo + return 0; +} + +/* + * Read one byte from memory. + */ +static int gdb_sys_mem_readb(struct GdbState *state, address addr, char *val) { + *val = *(volatile char *)addr; + return 0; +} + +static int gdb_sys_mem_read2b(struct GdbState *state, address addr, + NX_U16 *val) { + *val = *(volatile NX_U16 *)addr; + return 0; +} + +static int gdb_sys_mem_read4b(struct GdbState *state, address addr, + NX_U32 *val) { + *val = *(volatile NX_U32 *)addr; + return 0; +} + +/* + * Write one byte to memory. + */ +static int gdb_sys_mem_writeb(struct GdbState *state, address addr, char val) { + *(volatile char *)addr = val; + return 0; +} + +static int gdb_sys_mem_write2b(struct GdbState *state, address addr, + NX_U16 val) { + *(volatile NX_U16 *)addr = val; + return 0; +} + +static int gdb_sys_mem_write4b(struct GdbState *state, address addr, + NX_U32 val) { + *(volatile NX_U32 *)addr = val; + return 0; +} + + + +/* + * Write a sequence of bytes. + * + * Returns: + * 0 if successful + * GDB_EOF if failed to write all bytes + */ +static int gdb_write(struct GdbState *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 GdbState *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; +} + + +/***************************************************************************** + * 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 NX_NULL. + */ +static int gdb_strtoi(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 = NX_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 = 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 long(64bit) 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 NX_NULL. + */ +static long gdb_strtol(const char *str, unsigned int len, int base, + const char **endptr) { + unsigned int pos; + int sign, tmp, valid; + long value; + value = 0; + pos = 0; + sign = 1; + valid = 0; + + if (endptr) { + *endptr = NX_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 = 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; +} + +/* + * Compare two strings. + * + * Returns: + * 0 if the strings are equal + * 1 if the strings are not equal + */ +static int gdb_strncmp(const char *s1, const char *s2, NX_U32 n) { + while (n) { + if (*s1 != *s2 || !*s1 || !*s2) { + return *(unsigned char *)s1 - *(unsigned char *)s2; + } + s1++; + s2++; + n--; + } + return 0; +} + +/***************************************************************************** + * 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 GdbState *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 GdbState *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; + } + +#if DEBUG + { + unsigned int p; + GDB_PRINT("-> "); + for (p = 0; p < pkt_len; p++) { + if (gdb_is_printable_char(pkt_data[p])) { + GDB_PRINT("%c", pkt_data[p]); + } else { + GDB_PRINT("\\x%02x", pkt_data[p] & 0xff); + } + } + GDB_PRINT("\n"); + } +#endif + + /* 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 GdbState *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; + } + } + +#if DEBUG + { + unsigned int p; + GDB_PRINT("<- "); + for (p = 0; p < *pkt_len; p++) { + if (gdb_is_printable_char(pkt_buf[p])) { + GDB_PRINT("%c", pkt_buf[p]); + } else { + GDB_PRINT("\\x%02x", pkt_buf[p] & 0xff); + } + } + GDB_PRINT("\n"); + } +#endif + + /* 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 GdbState *state, char *buf, + unsigned int buf_len, address addr, unsigned int len, + gdb_enc_func enc) { + char data[4096]; + 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 GdbState *state, const char *buf, + unsigned int buf_len, address addr, unsigned int len, + gdb_dec_func dec) { + char data[4096]; + 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; +} + +static int find_pc_bp_num(struct GdbState *state) { + int bp_num = -1; + for (bp_num = 0; bp_num < __GDB_MAX_BREAKPOINTS_NUM_; bp_num++) { + if (gdb_bkp_buf[bp_num].en && + gdb_bkp_buf[bp_num].addr == state->registers[GDB_CPU_RISCV_REG_PC]) { + return bp_num; + } + } + return -1; +} + +/* + * Continue program execution at PC. + * Returns: + * 1 go to run debug program + * -1 error + */ +int gdb_continue(struct GdbState *state) { + // continue不需要考虑当前pc处是不是ebreak + return 1; +} + +/* + * Step one instruction. + */ +int gdb_step(struct GdbState *state) { + NX_U32 instruction = 0; + gdb_sys_mem_read4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC], + &instruction); + if ((instruction & 0x3) != 0x3) { + // compressed instruction 16 bits + int bp_num = -1; + if (instruction == gdb_c_ebreak_instruction) { + // ebreak instruction + bp_num = find_pc_bp_num(state); + if (bp_num == -1) { + return -1; + } + } + if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xa000) { + // c.j + int offset = 0; + offset |= ((instruction & 0x1000) >> 1) | ((instruction & 0x800) >> 7) | + ((instruction & 0x600) >> 1) | ((instruction & 0x100) << 2) | + ((instruction & 0x80) >> 1) | ((instruction & 0x40) << 1) | + ((instruction & 0x38) >> 2) | ((instruction & 0x4) << 3); + if (instruction & 0x1000) { + offset |= 0xfffff000; + } + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0x2000) { + // c.jal + int offset = 0; + offset |= ((instruction & 0x1000) >> 1) | ((instruction & 0x800) >> 7) | + ((instruction & 0x600) >> 1) | ((instruction & 0x100) << 2) | + ((instruction & 0x80) >> 1) | ((instruction & 0x40) << 1) | + ((instruction & 0x38) >> 2) | ((instruction & 0x4) << 3); + if (instruction & 0x1000) { + offset |= 0xfffff000; + } + state->registers[GDB_CPU_RISCV_REG_RA] = + state->registers[GDB_CPU_RISCV_REG_PC] + 2; + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + return 0; + } else if ((instruction & 0x7f) == 2 && (instruction & 0xf000) == 0x9000) { + // c.jalr + int tmp = state->registers[GDB_CPU_RISCV_REG_PC] + 2; + state->registers[GDB_CPU_RISCV_REG_PC] = + state->registers[(instruction & 0xf80) >> 7]; + state->registers[GDB_CPU_RISCV_REG_RA] = tmp; + return 0; + } else if ((instruction & 0x7f) == 2 && (instruction & 0xf000) == 0x8000) { + // c.jr + state->registers[GDB_CPU_RISCV_REG_PC] = + state->registers[(instruction & 0xf80) >> 7]; + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xc000) { + // c.beqz + int rs1 = (instruction & 0x380) >> 7; + if (state->registers[rs1 + 8] == 0) { + int offset = 0; + offset |= ((instruction & 0x1000) >> 4) | ((instruction & 0xc) >> 7) | + ((instruction & 0x60) << 1) | ((instruction & 0x18) >> 2) | + ((instruction & 0x4) << 3); + if (instruction & 0x100) { + offset |= 0xffffff00; + } + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xe000) { + // c.bnez + int rs1 = (instruction & 0x380) >> 7; + if (state->registers[rs1 + 8] != 0) { + int offset = 0; + offset |= ((instruction & 0x1000) >> 4) | ((instruction & 0xc) >> 7) | + ((instruction & 0x60) << 1) | ((instruction & 0x18) >> 2) | + ((instruction & 0x4) << 3); + if (instruction & 0x100) { + offset |= 0xffffff00; + } + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else { + // not control transfer instruction + gdb_sys_mem_write2b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC], + instruction); + if (bp_num != -1) + state->lastbp = bp_num; + else + state->lastbp = -1; + gdb_sys_mem_read4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC] + 2, + &instruction); + state->tmp_instruction = instruction; + gdb_sys_mem_write4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC] + 2, + gdb_ebreak_instruction); + return 1; + } + } else if ((instruction & 0x3) == 0x3 && (instruction & 0x1c) != 0x1c) { + // 32 bits instruction only consider isa set g(imafd) + int bp_num = -1; + if (instruction == gdb_ebreak_instruction) { + bp_num = find_pc_bp_num(state); + if (bp_num == -1) { + return -1; + } + } + + if ((instruction & 0x7f) == 0x6f) { + // jal + int rd = (instruction & 0xf80) >> 7; + state->registers[rd] = state->registers[GDB_CPU_RISCV_REG_PC] + 4; + int offset = 0; + offset |= ((instruction & 0x80000000) >> 11) | + ((instruction & 0x7fe00000) >> 20) | + ((instruction & 0x100000) >> 9) | (instruction & 0xff000); + if (instruction & 0x80000000) { + offset |= 0xfff00000; + } + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + return 0; + } else if ((instruction & 0x7f) == 0x67 && (instruction & 0x7000) == 0) { + // jalr + int rd = (instruction & 0xf80) >> 7; + int rs1 = (instruction & 0xf8000) >> 15; + state->registers[rd] = state->registers[GDB_CPU_RISCV_REG_PC] + 4; + int offset = 0; + offset |= ((instruction & 0xfff00000) >> 20); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + state->registers[GDB_CPU_RISCV_REG_PC] = state->registers[rs1] + offset; + state->registers[GDB_CPU_RISCV_REG_PC] &= ~1; + return 0; + } else if ((instruction & 0x7f) == 0x63 && (instruction & 0x7000) == 0) { + // beq + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (state->registers[rs1] == state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x1000) { + // bne + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (state->registers[rs1] != state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x4000) { + // blt + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if ((long)state->registers[rs1] < (long)state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x5000) { + // bge + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if ((long)state->registers[rs1] >= (long)state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x6000) { + // bltu + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (state->registers[rs1] < state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x7000) { + // bgeu + int rs1 = (instruction & 0xf8000) >> 15; + int rs2 = (instruction & 0x1f00000) >> 20; + int offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (state->registers[rs1] >= state->registers[rs2]) { + state->registers[GDB_CPU_RISCV_REG_PC] += offset; + } + return 0; + } else { + // not control transfer instruction + gdb_sys_mem_write4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC], + instruction); + if (bp_num != -1) + state->lastbp = bp_num; + else + state->lastbp = -1; + gdb_sys_mem_read4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC] + 4, + &instruction); + state->tmp_instruction = instruction; + gdb_sys_mem_write4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC] + 4, + gdb_ebreak_instruction); + return 1; + } + } else { + return -1; + } + return 0; +} + +/***************************************************************************** + * Packet Creation Helpers + ****************************************************************************/ + +/* + * Send OK packet + */ +static int gdb_send_ok_packet(struct GdbState *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 GdbState *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 GdbState *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 GdbState *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); +} + +/* + * 目前支持部分查询,并返回固定特性字符串 + */ +static int gdb_handle_query(GdbState *state, char pkt_buf[PKT_BUF_LEN], + NX_U32 pkt_len) { + const char *ptrNext; + ptrNext = pkt_buf + 1; + if (gdb_strncmp(ptrNext, "Supported", 9) == 0) { + return gdb_send_packet(state, "swbreak+;vContSupported+;", 25); + } else if (gdb_strncmp(ptrNext, "TStatus", 7) == 0) { + return gdb_send_packet(state, NX_NULL, 0); + } else if (gdb_strncmp(ptrNext, "fThreadInfo", 11) == 0) { + return gdb_send_packet(state, "m1", 2); + } else if (gdb_strncmp(ptrNext, "sThreadInfo", 11) == 0) { + return gdb_send_packet(state, "l", 1); + } else if (gdb_strncmp(ptrNext, "Attached", 8) == 0) { + return gdb_send_packet(state, "1", 1); + } else if (gdb_strncmp(ptrNext, "C", 1) == 0) { + return gdb_send_packet(state, "QC1", 3); + } else if (gdb_strncmp(ptrNext, "Offsets", 7) == 0) { + return gdb_send_packet(state, NX_NULL, 0); + } else if (gdb_strncmp(ptrNext, "Symbol::", 8) == 0) { + return gdb_send_packet(state, "OK", 2); + } else { + return GDB_EOF; + } +} + +static int gdb_add_break_point(NX_U32 type, NX_U64 addr, NX_U32 length) { + NX_U16 i; + for (i = 0; i < __GDB_MAX_BREAKPOINTS_NUM_; i++) { + if (!gdb_bkp_buf[i].en) { + gdb_bkp_buf[i].en = 1; + gdb_bkp_buf[i].type = type; + gdb_bkp_buf[i].addr = addr; + gdb_bkp_buf[i].len = length; + if (length == 2) { + gdb_sys_mem_read2b(NX_NULL, addr, (NX_U16 *)&gdb_bkp_buf[i].instruction); + // insert c.ebreak instruction + gdb_sys_mem_write2b(NX_NULL, addr, gdb_c_ebreak_instruction); + } else if (length == 4) { + gdb_sys_mem_read4b(NX_NULL, addr, (NX_U32 *)&gdb_bkp_buf[i].instruction); + // insert ebreak instruction + gdb_sys_mem_write4b(NX_NULL, addr, gdb_ebreak_instruction); + } else { + return -1; + } + return 1; + } + } + return 0; +} + +static int gdb_handle_point(GdbState *state, NX_U32 type, NX_U64 addr, + NX_U32 length) { + // insert break or watchpoint (draft) + // Zt,addr,length + // t is type: `0' - software breakpoint, `1' - hardware breakpoint, `2' - + // write watchpoint, `3' - read watchpoint, `4' - access watchpoint; addr is + // address; length is in bytes. For a software breakpoint, length specifies + // the size of the instruction to be patched. For hardware breakpoints and + // watchpoints length specifies the memory region to be monitored. To avoid + // potential problems with duplicate packets, the operations should be + // implemented in an idempotent way. + // reply ENN for an error reply OK for success + // `' If not supported. + switch (type) { + case 0: + // software breakpoint + case 1: + // hardware breakpoint + if (gdb_add_break_point(type, addr, length) == 0) { + return -1; + } else { + return 0; + } + break; + case 2: + // write watchpoint + break; + case 3: + // read watchpoint + break; + case 4: + // access watchpoint + break; + default: + return -1; + } + return -1; +} + +static int gdb_remove_break_point(NX_U32 type, NX_U64 addr, NX_U32 length) { + NX_U16 i; + for (i = 0; i < __GDB_MAX_BREAKPOINTS_NUM_; i++) { + if (gdb_bkp_buf[i].en && gdb_bkp_buf[i].type == type && + gdb_bkp_buf[i].addr == addr && gdb_bkp_buf[i].len == length) { + gdb_bkp_buf[i].en = 0; + if (length == 2) { + gdb_sys_mem_write2b(NX_NULL, addr, gdb_bkp_buf[i].instruction); + } else if (length == 4) { + gdb_sys_mem_write4b(NX_NULL, addr, gdb_bkp_buf[i].instruction); + } else { + return -1; + } + return 1; + } + } + return 0; +} + +static int gdb_remove_point(GdbState *state, NX_U32 type, NX_U64 addr, + NX_U32 length) { + // remove break or watchpoint + switch (type) { + case 0: + // software breakpoint + case 1: + // hardware breakpoint + if (gdb_remove_break_point(type, addr, length) == 0) { + return -1; + } else { + return 0; + } + break; + case 2: + // write watchpoint + break; + case 3: + // read watchpoint + break; + case 4: + // access watchpoint + break; + default: + return -1; + } + return -1; +} + +/* + * Main debug loop. Handles commands. + */ +static int gdb_main(struct GdbState *state) { + address addr; + char pktBuf[PKT_BUF_LEN]; + int status; + NX_U32 length; + NX_U32 pktLen; + const char *ptrNext; + NX_U32 type; + + if (isStartEbreakExectued == 0) { + // 第一次通过ebreak,与gdb进行初始化的一些信息发送 + isStartEbreakExectued = 1; + state->registers[GDB_CPU_RISCV_REG_PC] += 2; + } else { + // 不是第一次控制权交给stub, 故而要告诉gdb为什么程序停下来了 + gdb_send_signal_packet(state, pktBuf, sizeof(pktBuf), state->signum); + } + + if (state->lastbp != -2) { + // step command end + if (state->lastbp >= 0) { + if (gdb_bkp_buf[state->lastbp].len == 2) { + gdb_sys_mem_write2b(NX_NULL, gdb_bkp_buf[state->lastbp].addr, + gdb_c_ebreak_instruction); + } else if (gdb_bkp_buf[state->lastbp].len == 4) { + gdb_sys_mem_write4b(NX_NULL, gdb_bkp_buf[state->lastbp].addr, + gdb_ebreak_instruction); + } else { + goto error; + } + state->lastbp = -2; + } + gdb_sys_mem_write4b(NX_NULL, state->registers[GDB_CPU_RISCV_REG_PC], + state->tmp_instruction); + } + + while (1) { + /* Receive the next packet */ + status = gdb_recv_packet(state, pktBuf, sizeof(pktBuf), &pktLen); + if (status == GDB_EOF) { + break; + } + + if (pktLen == 0) { + /* Received empty packet.. */ + continue; + } + + ptrNext = pktBuf; + + /* + * Handle one letter commands + */ + switch (pktBuf[0]) { + +/* Calculate remaining space in packet from ptrNext position. */ +#define token_remaining_buf (pktLen - (ptrNext - pktBuf)) + +/* Expecting a seperator. If not present, go to error */ +#define token_expect_seperator(c) \ + { \ + if (!ptrNext || *ptrNext != c) { \ + goto error; \ + } else { \ + ptrNext += 1; \ + } \ + } + +/* Expecting an unsigned integer argument. If not present, go to error */ +#define token_expect_uinteger_arg(arg) \ + { \ + arg = (NX_U32)gdb_strtoi(ptrNext, token_remaining_buf, 16, &ptrNext); \ + if (!ptrNext) { \ + goto error; \ + } \ + } + +/* Expecting an long argument. If not present, go to error */ +#define token_expect_ulong_arg(arg) \ + { \ + arg = (NX_U64)gdb_strtol(ptrNext, token_remaining_buf, 16, &ptrNext); \ + if (!ptrNext) { \ + goto error; \ + } \ + } + + /* + * Read Registers + * Command Format: g + */ + case 'g': + /* Encode registers */ + status = + gdb_enc_hex(pktBuf, sizeof(pktBuf), (char *)&(state->registers), + sizeof(state->registers)); + if (status == GDB_EOF) { + goto error; + } + pktLen = status; + gdb_send_packet(state, pktBuf, pktLen); + break; + + /* + * Write Registers + * Command Format: G XX... + */ + case 'G': + status = + gdb_dec_hex(pktBuf + 1, pktLen - 1, (char *)&(state->registers), + sizeof(state->registers)); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + break; + + /* + * Read a Register + * Command Format: p n + */ + case 'p': + ptrNext += 1; + token_expect_uinteger_arg(addr); + + if (addr >= GDB_CPU_NUM_REGISTERS) { + goto error; + } + + /* Read Register */ + status = gdb_enc_hex(pktBuf, sizeof(pktBuf), + (char *)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == GDB_EOF) { + goto error; + } + gdb_send_packet(state, pktBuf, status); + break; + + /* + * Write a Register + * Command Format: P n...=r... + */ + case 'P': + ptrNext += 1; + token_expect_uinteger_arg(addr); + token_expect_seperator('='); + + if (addr < GDB_CPU_NUM_REGISTERS) { + status = gdb_dec_hex(ptrNext, token_remaining_buf, + (char *)&(state->registers[addr]), + sizeof(state->registers[addr])); + if (status == GDB_EOF) { + goto error; + } + } + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + break; + + /* + * Read Memory + * Command Format: m addr,length + */ + case 'm': + ptrNext += 1; + token_expect_uinteger_arg(addr); + token_expect_seperator(','); + token_expect_uinteger_arg(length); + /* Read Memory */ + status = gdb_mem_read(state, pktBuf, sizeof(pktBuf), addr, length, + gdb_enc_hex); + if (status == GDB_EOF) { + goto error; + } + gdb_send_packet(state, pktBuf, status); + break; + + /* + * Write Memory + * Command Format: M addr,length:XX.. + */ + case 'M': + ptrNext += 1; + token_expect_uinteger_arg(addr); + token_expect_seperator(','); + token_expect_uinteger_arg(length); + token_expect_seperator(':'); + + /* Write Memory */ + status = gdb_mem_write(state, ptrNext, token_remaining_buf, addr, length, + gdb_dec_hex); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + break; + + /* + * Write Memory (Binary) + * Command Format: X addr,length:XX.. + */ + case 'X': + ptrNext += 1; + token_expect_uinteger_arg(addr); + token_expect_seperator(','); + token_expect_uinteger_arg(length); + token_expect_seperator(':'); + + /* Write Memory */ + status = gdb_mem_write(state, ptrNext, token_remaining_buf, addr, length, + gdb_dec_bin); + if (status == GDB_EOF) { + goto error; + } + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + break; + + /* + * Continue + * Command Format: c [addr] + */ + case 'c': + status = gdb_continue(state); + if (status == 1) { + return 0; + } else if (status == 0) { + break; + } else { + goto error; + } + + /* + * Single-step + * Command Format: s [addr] + */ + case 's': + status = gdb_step(state); + if (status == 1) { + return 0; + } else if (status == 0) { + gdb_send_signal_packet(state, pktBuf, sizeof(pktBuf), state->signum); + break; + } else { + goto error; + } + + case '?': + gdb_send_signal_packet(state, pktBuf, sizeof(pktBuf), state->signum); + break; + + /* + * Query + * Command Format: q + */ + case 'q': + status = gdb_handle_query(state, pktBuf, pktLen); + if (status == GDB_EOF) + goto error; + break; + + case 'H': + // 只针对单线程,默认返回OK + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + break; + + case 'z': + // 删除断点 + ptrNext += 1; + token_expect_uinteger_arg(type); + token_expect_seperator(','); + token_expect_ulong_arg(addr); + token_expect_seperator(','); + token_expect_uinteger_arg(length); + if (gdb_remove_point(state, type, addr, length) == 0) { + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + } else { + goto error; + } + break; + + case 'Z': + // 插入断点 + ptrNext += 1; + token_expect_uinteger_arg(type); + token_expect_seperator(','); + token_expect_ulong_arg(addr); + token_expect_seperator(','); + token_expect_uinteger_arg(length); + if (gdb_handle_point(state, type, addr, length) == 0) { + gdb_send_ok_packet(state, pktBuf, sizeof(pktBuf)); + } else { + goto error; + } + break; + + case 'v': + ptrNext += 1; + if (gdb_strncmp(ptrNext, "Cont?", 5) == 0) { + gdb_send_packet(state, "vCont;c;s", 9); + } else if (gdb_strncmp(ptrNext, "MustReplyEmpty", 14) == 0) { + gdb_send_packet(state, "", 0); + } + break; + /* + * Unsupported Command + */ + default: + gdb_send_packet(state, NX_NULL, 0); + } + + continue; + + error: + gdb_send_error_packet(state, pktBuf, sizeof(pktBuf), 0x00); + +#undef token_remaining_buf +#undef token_expect_seperator +#undef token_expect_uinteger_arg +#undef token_expect_ulong_arg + } + + return 0; +} + +void GdbStubTrapHandler(NX_HalTrapFrame *frame) +{ + /* Load Registers */ + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_X0] = 0; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_RA] = frame->ra; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_SP] = frame->sp; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_GP] = frame->gp; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_TP] = frame->tp; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T0] = frame->t0; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T1] = frame->t1; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T2] = frame->t2; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S0] = frame->s0; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S1] = frame->s1; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A0] = frame->a0; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A1] = frame->a1; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A2] = frame->a2; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A3] = frame->a3; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A4] = frame->a4; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A5] = frame->a5; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A6] = frame->a6; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A7] = frame->a7; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S2] = frame->s2; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S3] = frame->s3; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S4] = frame->s4; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S5] = frame->s5; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S6] = frame->s6; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S7] = frame->s7; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S8] = frame->s8; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S9] = frame->s9; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S10] = frame->s10; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S11] = frame->s11; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T3] = frame->t3; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T4] = frame->t4; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T5] = frame->t5; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T6] = frame->t6; + gdbStateGlobal.registers[GDB_CPU_RISCV_REG_PC] = frame->epc; + + gdb_main(&gdbStateGlobal); + + /* Store Registers */ + frame->ra = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_RA]; + frame->sp = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_SP]; + frame->gp = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_GP]; + frame->tp = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_TP]; + frame->t0 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T0]; + frame->t1 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T1]; + frame->t2 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T2]; + frame->s0 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S0]; + frame->s1 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S1]; + frame->a0 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A0]; + frame->a1 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A1]; + frame->a2 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A2]; + frame->a3 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A3]; + frame->a4 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A4]; + frame->a5 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A5]; + frame->a6 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A6]; + frame->a7 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_A7]; + frame->s2 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S2]; + frame->s3 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S3]; + frame->s4 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S4]; + frame->s5 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S5]; + frame->s6 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S6]; + frame->s7 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S7]; + frame->s8 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S8]; + frame->s9 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S9]; + frame->s10 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S10]; + frame->s11 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_S11]; + frame->t3 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T3]; + frame->t4 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T4]; + frame->t5 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T5]; + frame->t6 = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_T6]; + frame->epc = gdbStateGlobal.registers[GDB_CPU_RISCV_REG_PC]; +} + + + +void gdb_sys_init(void) { + NX_LOG_I("gdb stub init"); + for (int i = 0; i < __GDB_MAX_BREAKPOINTS_NUM_; i++) { + gdb_bkp_buf[i].en = 0; + } + + // receive init '+' + while(gdb_sys_getc(NX_NULL) != '+') { + + } + gdbStateGlobal.signum = 5; + gdbStateGlobal.lastbp = -2; + isStartEbreakExectued = 0; + // __asm__ volatile("ebreak"); +} +#ifdef NX_DEBUG_STUB +NX_INITCALL(gdb_sys_init); +#endif \ No newline at end of file diff --git a/src/arch/riscv64/kernel/ptrac.c b/src/arch/riscv64/kernel/ptrac.c new file mode 100644 index 0000000000000000000000000000000000000000..116bb640c022c1996e451b915ff05588bdd71460 --- /dev/null +++ b/src/arch/riscv64/kernel/ptrac.c @@ -0,0 +1,337 @@ +#include "base/console.h" +#include "base/thread.h" +#include "base/process.h" +#include "base/uaccess.h" + +#include "ptrace.h" +#include "irq.h" +#include "arch/process.h" + +NX_I32 SingleStepCallBack(NX_Thread * thread, NX_HalTrapFrame * trapFrame) +{ + NX_VmspaceWrite(&thread->resource.process->vmspace, (char*)trapFrame->epc, (char*)&thread->singleStepNextInstruction, sizeof(NX_U32)); + return 0; +} + +NX_I32 StartSingleStep(NX_Thread * thread){ + NX_U32 instruction; + NX_VmspaceRead(&thread->resource.process->vmspace, (char*)NX_USER_IMAGE_VADDR, (char*)&instruction, sizeof(NX_U32)); + thread->singleStepNextInstruction = instruction; + NX_U32 ebreak = gdb_ebreak_instruction; + NX_VmspaceWrite(&thread->resource.process->vmspace, (char*)NX_USER_IMAGE_VADDR, (char*)&ebreak, sizeof(NX_U32)); + return 0; +} +/** +* return value: +* 0: single step success +* 1: the instruction is not control transfer instruction, so gdb should wait for child process stop +*/ +NX_I32 SingleStep(NX_Thread * thread, NX_HalTrapFrame * trapFrame) +{ + NX_U32 instruction = 0; + NX_VmspaceRead(&thread->resource.process->vmspace, (char*)trapFrame->epc, (char*)&instruction, sizeof(NX_U32)); + if ((instruction & 0x3) != 0x3) { + // compressed instruction 16 bits + if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xa000) { + // c.j + NX_I32 offset = 0; + offset |= ((instruction & 0x1000) >> 1) | ((instruction & 0x800) >> 7) | + ((instruction & 0x600) >> 1) | ((instruction & 0x100) << 2) | + ((instruction & 0x80) >> 1) | ((instruction & 0x40) << 1) | + ((instruction & 0x38) >> 2) | ((instruction & 0x4) << 3); + if (instruction & 0x1000) { + offset |= 0xfffff000; + } + trapFrame->epc += offset; + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0x2000) { + // c.jal + NX_I32 offset = 0; + offset |= ((instruction & 0x1000) >> 1) | ((instruction & 0x800) >> 7) | + ((instruction & 0x600) >> 1) | ((instruction & 0x100) << 2) | + ((instruction & 0x80) >> 1) | ((instruction & 0x40) << 1) | + ((instruction & 0x38) >> 2) | ((instruction & 0x4) << 3); + if (instruction & 0x1000) { + offset |= 0xfffff000; + } + trapFrame->ra = trapFrame->epc + 2; + trapFrame->epc += offset; + return 0; + } else if ((instruction & 0x7f) == 2 && (instruction & 0xf000) == 0x9000) { + // c.jalr + NX_I32 tmp = trapFrame->epc + 2; + NX_I32 rs1 = (instruction & 0xf80) >> 7; + if(rs1 == 0){ + return -NX_EINVAL; + } + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + trapFrame->epc = *(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)); + trapFrame->ra = tmp; + return 0; + } else if ((instruction & 0x7f) == 2 && (instruction & 0xf000) == 0x8000) { + // c.jr + NX_I32 rs1 = (instruction & 0xf80) >> 7; + if(rs1 == 0){ + return -NX_EINVAL; + } + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + trapFrame->epc = *(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)); + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xc000) { + // c.beqz + NX_I32 rs1 = (instruction & 0x380) >> 7; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + if (*(NX_UArch*)(tmpTrapFrame + (rs1 + 8) * sizeof(NX_UArch)) == 0) { + NX_I32 offset = 0; + offset |= ((instruction & 0x1000) >> 4) | ((instruction & 0xc) >> 7) | + ((instruction & 0x60) << 1) | ((instruction & 0x18) >> 2) | + ((instruction & 0x4) << 3); + if (instruction & 0x100) { + offset |= 0xffffff00; + } + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x3) == 1 && (instruction & 0xe000) == 0xe000) { + // c.bnez + NX_I32 rs1 = (instruction & 0x380) >> 7; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + if (*(NX_UArch*)(tmpTrapFrame + (rs1 + 8) * sizeof(NX_UArch)) == 0) { + NX_I32 offset = 0; + offset |= ((instruction & 0x1000) >> 4) | ((instruction & 0xc) >> 7) | + ((instruction & 0x60) << 1) | ((instruction & 0x18) >> 2) | + ((instruction & 0x4) << 3); + if (instruction & 0x100) { + offset |= 0xffffff00; + } + trapFrame->epc += offset; + } + return 0; + } else { + // not control transfer instruction + NX_VmspaceRead(&thread->resource.process->vmspace, (char*)trapFrame->epc + 2, (char*)&instruction, sizeof(NX_U32)); + thread->singleStepNextInstruction = instruction; + NX_U32 ebreak = gdb_ebreak_instruction; + NX_VmspaceWrite(&thread->resource.process->vmspace, (char*)trapFrame->epc + 2, (char*)&ebreak, sizeof(NX_U32)); + NX_ThreadUnblock(thread); + return 1; + } + }else if ((instruction & 0x3) == 0x3 && (instruction & 0x1c) != 0x1c) { + // 32 bits instruction only consider isa set g(imafd) + if ((instruction & 0x7f) == 0x6f) { + // jal + NX_I32 rd = (instruction & 0xf80) >> 7; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + *(NX_UArch*)(tmpTrapFrame + rd * sizeof(NX_UArch)) = trapFrame->epc + 4; + NX_I32 offset = 0; + offset |= ((instruction & 0x80000000) >> 11) | + ((instruction & 0x7fe00000) >> 20) | + ((instruction & 0x100000) >> 9) | (instruction & 0xff000); + if (instruction & 0x80000000) { + offset |= 0xfff00000; + } + trapFrame->epc += offset; + return 0; + } else if ((instruction & 0x7f) == 0x67 && (instruction & 0x7000) == 0) { + // jalr + NX_I32 rd = (instruction & 0xf80) >> 7; + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + *(NX_UArch*)(tmpTrapFrame + rd * sizeof(NX_UArch)) = trapFrame->epc + 4; + + NX_I32 offset = 0; + offset |= ((instruction & 0xfff00000) >> 20); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + trapFrame->epc = *(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) + offset; + trapFrame->epc &= ~1; + return 0; + } else if ((instruction & 0x7f) == 0x63 && (instruction & 0x7000) == 0) { + // beq + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) == + *(NX_UArch*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x1000) { + // bne + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) != + *(NX_UArch*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x4000) { + // blt + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_I64*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) < + *(NX_I64*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x5000) { + // bge + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_I64*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) >= + *(NX_I64*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x6000) { + // bltu + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) < + *(NX_UArch*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else if ((instruction & 0x7f) == 0x63 && + (instruction & 0x7000) == 0x7000) { + // bgeu + NX_I32 rs1 = (instruction & 0xf8000) >> 15; + NX_I32 rs2 = (instruction & 0x1f00000) >> 20; + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + NX_I32 offset = 0; + offset |= + ((instruction & 0xf00) >> 7) | ((instruction & 0x80000000) >> 19) | + ((instruction & 0x7e000000) >> 20) | ((instruction & 0x80) << 4); + if (instruction & 0x80000000) { + offset |= 0xfffff000; + } + if (*(NX_UArch*)(tmpTrapFrame + rs1 * sizeof(NX_UArch)) >= + *(NX_UArch*)(tmpTrapFrame + rs2 * sizeof(NX_UArch))) { + trapFrame->epc += offset; + } + return 0; + } else { + // not control transfer instruction + NX_VmspaceRead(&thread->resource.process->vmspace, (char*)trapFrame->epc + 4, (char*)&instruction, sizeof(NX_U32)); + thread->singleStepNextInstruction = instruction; + NX_U32 ebreak = gdb_ebreak_instruction; + NX_VmspaceWrite(&thread->resource.process->vmspace, (char*)trapFrame->epc + 4, (char*)&ebreak, sizeof(NX_U32)); + NX_ThreadUnblock(thread); + return 1; + } + } else { + return -1; + } + return 0; +} + +NX_Error Ptrace(enum __ptrace_request request, NX_Thread* target, void * addr, void * data) +{ + NX_Process * process; + process = NX_ProcessCurrent(); + NX_U64 buf; + NX_HalTrapFrame * trapFrame; + trapFrame = (NX_HalTrapFrame*)(target->stackBase + target->stackSize - sizeof(NX_HalTrapFrame)); + NX_U64 tmpTrapFrame = (NX_U64)trapFrame; + + switch (request) { + case PTRACE_PEEKINT8: + NX_VmspaceRead(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U8)); + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U8)); + break; + case PTRACE_PEEKINT16: + NX_VmspaceRead(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U16)); + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U16)); + break; + case PTRACE_PEEKINT32: + NX_VmspaceRead(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U32)); + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U32)); + break; + case PTRACE_PEEKINT64: + NX_VmspaceRead(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U64)); + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U64)); + break; + case PTRACE_POKEINT8: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U8)); + NX_VmspaceWrite(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U8)); + break; + case PTRACE_POKEINT16: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U16)); + NX_VmspaceWrite(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U16)); + break; + case PTRACE_POKEINT32: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U32)); + NX_VmspaceWrite(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U32)); + break; + case PTRACE_POKEINT64: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)&buf, sizeof(NX_U64)); + NX_VmspaceWrite(&target->resource.process->vmspace, (char*)addr, (char*)&buf, sizeof(NX_U64)); + break; + case PTRACE_CONT: + NX_ThreadUnblock(target); + break; + case PTRACE_KILL: + NX_SignalSend(target->tid, NX_SIGNAL_KILL, NX_NULL, NX_NULL); + break; + case PTRACE_SINGLESTEP: + return SingleStep(target, trapFrame); + break; + case PTRACE_GETREGS: + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)trapFrame, sizeof(Registers)); + break; + case PTRACE_SETREGS: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)trapFrame, sizeof(Registers)); + break; + case PTRACE_GETFPREGS: + NX_VmspaceWrite(&process->vmspace, (char*)data, (char*)(tmpTrapFrame + sizeof(Registers) + sizeof(NX_UArch)), sizeof(FpRegisters)); + break; + case PTRACE_SETFPREGS: + NX_VmspaceRead(&process->vmspace, (char*)data, (char*)(tmpTrapFrame + sizeof(Registers) + sizeof(NX_UArch)), sizeof(FpRegisters)); + break; + } + return NX_EOK; +} \ No newline at end of file diff --git a/src/arch/riscv64/kernel/trap.c b/src/arch/riscv64/kernel/trap.c index edb9e2d193dacb5d9be192e4ae9c6a2951f0e917..bad31d8685b4cff3be2af313ae6121b9a0a0416b 100644 --- a/src/arch/riscv64/kernel/trap.c +++ b/src/arch/riscv64/kernel/trap.c @@ -15,6 +15,7 @@ #include #include #include +#include #define NX_LOG_NAME "Trap" #define NX_LOG_LEVEL NX_LOG_INFO @@ -26,6 +27,7 @@ #include #include #include +#include "ptrace.h" #define RISCV_INS_ADDR_MISALIGNED 0 #define RISCV_INS_ACCESS_FAULT 1 @@ -282,7 +284,18 @@ void TrapDispatch(NX_HalTrapFrame *frame) return; case RISCV_BREAKPOINT: NX_LOG_E("riscv exception %s occur", msg); - NX_SignalSend(self->tid, NX_SIGNAL_TRAP, NX_NULL, NX_NULL); +#ifdef NX_DEBUG_STUB + GdbStubTrapHandler(frame); +#else + if(NX_SignalSend(self->resource.process->parentPid, NX_SIGNAL_CHILD, NX_NULL, NX_NULL) != 0){ + NX_Printf("send signal error\n"); + } + if(self->singleStepNextInstruction != 0){ + SingleStepCallBack(self, frame); + self->singleStepNextInstruction = 0; + } + NX_ThreadBlock(self); +#endif return; case RISCV_INS_ADDR_MISALIGNED: case RISCV_INS_ACCESS_FAULT: diff --git a/src/include/base/process.h b/src/include/base/process.h index 9ab6323854980c3a896a4b155bee1baaaee3f438..220480630e82ceb01821735debf9d87aac2626e6 100644 --- a/src/include/base/process.h +++ b/src/include/base/process.h @@ -54,6 +54,8 @@ struct NX_Process char cwd[NX_FILE_MAX_PATH]; /* current work diretory */ char exePath[NX_FILE_MAX_PATH]; /* execute path */ NX_ExposedObjectTable exobjTable; + + NX_U32 ptrace; }; typedef struct NX_Process NX_Process; diff --git a/src/include/base/ptrace.h b/src/include/base/ptrace.h new file mode 100644 index 0000000000000000000000000000000000000000..831a5b3a4e6ee58368ad1e49daeb70884029ba38 --- /dev/null +++ b/src/include/base/ptrace.h @@ -0,0 +1,46 @@ +#ifndef __PTRACE_H__ +#define __PTRACE_H__ + + +enum __ptrace_request{ + + PTRACE_PEEKINT8 = 0, + + PTRACE_PEEKINT16 = 1, + + PTRACE_PEEKINT32 = 2, + + PTRACE_PEEKINT64 = 3, + + PTRACE_POKEINT8 = 4, + + PTRACE_POKEINT16 = 5, + + PTRACE_POKEINT32 = 6, + + PTRACE_POKEINT64 = 7, + + /* Continue the process. */ + PTRACE_CONT = 8, + + /* Kill the process. */ + PTRACE_KILL = 9, + + /* Single step the process. */ + PTRACE_SINGLESTEP = 10, + + /* Get all general purpose registers used by a processes. */ + PTRACE_GETREGS = 11, + + /* Set all general purpose registers used by a processes. */ + PTRACE_SETREGS = 12, + + /* Get all floating point registers used by a processes. */ + PTRACE_GETFPREGS = 13, + + /* Set all floating point registers used by a processes. */ + PTRACE_SETFPREGS = 14, + +}; + +#endif \ No newline at end of file diff --git a/src/include/base/signal.h b/src/include/base/signal.h index 565aecaa2eb2876f10501cb35420f598ddfabe23..6085e8d5ba41c0827efb5866230ebaeffa19ea1f 100644 --- a/src/include/base/signal.h +++ b/src/include/base/signal.h @@ -34,6 +34,7 @@ #define NX_SIGNAL_SEGV 14 /* segment fault */ #define NX_SIGNAL_ILLEGAL 15 /* illegal instruction */ #define NX_SIGNAL_INST 16 /* instruction aceesss */ +#define NX_SIGNAL_CHILD 17 /* child process */ /* ...-31 RESERVED */ #define NX_SIGNAL_ANONYMOUS 32 /* anonymous signal */ diff --git a/src/include/base/thread.h b/src/include/base/thread.h index d399176f5b25c03aba9397a22a4539e5f2091f41..3a455dc7b2ea6992bac24614feb60da86f5ca35d 100644 --- a/src/include/base/thread.h +++ b/src/include/base/thread.h @@ -83,6 +83,7 @@ #define NX_THREAD_CREATE_NORMAL 0x00 /* thread create with normal flag(no wait, no suspend, running) */ #define NX_THREAD_CREATE_SUSPEND 0x01 /* thread create with suspend flag(no running) */ #define NX_THREAD_CREATE_WAIT 0x02 /* thread create with wait thread exit(no return, must wait exit) */ +#define NX_THREAD_CREATE_DEBUG 0x04 /* thread create to be debugged by gdb */ #define NX_THREAD_FLAG_SIGNAL_INTR 0x01 /* thread was interrupted by signal */ #define NX_THREAD_FLAG_UNINTERRUPTABLE 0x02 /* thread was uninterrupted by signal */ @@ -167,6 +168,7 @@ struct NX_Thread NX_UArch onCore; /* thread on which core */ NX_UArch coreAffinity; /* thread would like to run on the core */ + NX_U32 singleStepNextInstruction; /* next single step instruction */ /* thread resource */ NX_ThreadResource resource; }; diff --git a/src/kernel/Kconfig b/src/kernel/Kconfig index 780453c549bb81d0ffc9a9651b4d9cca102f1e4b..986633d17181825ff41fb4057a600a541c089ebc 100644 --- a/src/kernel/Kconfig +++ b/src/kernel/Kconfig @@ -22,6 +22,10 @@ if NX_DEBUG endif +config NX_DEBUG_STUB + bool "Stub for debug" + default n + endmenu menu "Hook system" diff --git a/src/process/process.c b/src/process/process.c index a987db86f64275c50ff33cccc96265c38ccf649d..ca0148003fb3c1c7dbddeb7ea25de643685f81aa 100644 --- a/src/process/process.c +++ b/src/process/process.c @@ -31,6 +31,7 @@ #include #include #include +#include "ptrace.h" NX_PRIVATE NX_Error NX_ProcessWait(NX_Process * process, NX_U32 *exitCode); @@ -130,6 +131,8 @@ NX_PRIVATE NX_Process *NX_ProcessCreateObject(NX_U32 flags) NX_ThreadUsageInit(&process->usageInfo); + process->ptrace = 0; + return process; } @@ -833,6 +836,9 @@ NX_Error NX_ProcessLaunch(char *path, NX_U32 flags, NX_U32 *exitCode, if (!(flags & NX_THREAD_CREATE_SUSPEND)) { + if(flags & NX_THREAD_CREATE_DEBUG){ + StartSingleStep(thread); + } if (NX_ThreadStart(thread) != NX_EOK) { if (self->resource.process) diff --git a/src/process/syscall.c b/src/process/syscall.c index 0852e1314c64b1dfa69ac392a240d5558d55f3c0..effc4efa8e5dd306e1353ede407a61b4aa7d5c2b 100644 --- a/src/process/syscall.c +++ b/src/process/syscall.c @@ -36,7 +36,8 @@ #include #include #include - +#include +#include "ptrace.h" #include "process_impl.h" NX_PRIVATE int SysInvalidCall(void) @@ -2898,6 +2899,31 @@ NX_Error SysSoltCopy(NX_Solt dest, NX_Solt solt, NX_Solt * outSolt) return err; } +NX_Error SysPtrace(enum __ptrace_request request, NX_Solt solt, void * addr, void * data) +{ + NX_ExposedObject * exobj; + NX_Thread * thread; + + if (solt == NX_SOLT_INVALID_VALUE) + { + return NX_EINVAL; + } + + exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt); + if (exobj == NX_NULL) + { + return NX_ENOSRCH; + } + + if (exobj->type != NX_EXOBJ_THREAD) + { + return NX_ENORES; + } + thread = (NX_Thread *)exobj->object; + NX_ASSERT(thread); + return Ptrace(request, thread, addr, data); +} + NX_PRIVATE NX_Error ConditionCloseSolt(void * object, NX_ExposedObjectType type) { NX_Condition * condition; @@ -3182,6 +3208,7 @@ NX_PRIVATE const NX_SyscallHandler NX_SyscallTable[] = SysConditionWaitTimeout, SysConditionSignal, /* 100 */ SysConditionBroadcast, + SysPtrace, }; /* posix env syscall table */ diff --git a/src/sched/thread.c b/src/sched/thread.c index 5bde5ca81dc8ab8049a71dc43039e30957f306cf..6ddf4b11d657a477bcdec3dc7d5c2ac16e1e684b 100644 --- a/src/sched/thread.c +++ b/src/sched/thread.c @@ -76,6 +76,8 @@ NX_PRIVATE NX_Error ThreadInit(NX_Thread *thread, thread->onCore = NX_MULTI_CORES_NR; /* not on any core */ thread->coreAffinity = NX_MULTI_CORES_NR; /* no core affinity */ + thread->singleStepNextInstruction = 0; + thread->resource.sleepTimer = NX_NULL; thread->resource.process = NX_NULL; thread->resource.fileTable = NX_FileTableGetDefault();