/**************************************************************************** * libs/libc/gdbstub/lib_gdbstub.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifdef CONFIG_LIB_GDBSTUB_DEBUG # define GDB_DEBUG(...) syslog(LOG_DEBUG, ##__VA_ARGS__) # define GDB_ASSERT() __assert(__FILE__, __LINE__, 0) #else # define GDB_DEBUG(...) # define GDB_ASSERT() do {} while (0) #endif /**************************************************************************** * Private Types ****************************************************************************/ struct gdb_state_s { gdb_send_func_t send; /* Send buffer to gdb */ gdb_recv_func_t recv; /* Recv buffer from gdb */ FAR void *priv; /* Private data for transport */ gdb_monitor_func_t monitor; /* Monitor will be called when gdb * receive a monitor command */ pid_t pid; /* Gdb current thread */ FAR char *pkt_next; /* Pointer to next byte in packet */ char pkt_buf[1024]; /* Packet buffer */ size_t pkt_len; /* Packet send and receive length */ uint8_t running_regs[XCPTCONTEXT_SIZE]; /* Registers of running thread */ size_t size; /* Size of registers */ uintptr_t registers[0]; /* Registers of other threads */ }; typedef CODE ssize_t (*gdb_format_func_t)(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len); /* Calculate remaining space in packet from ptr_next position. */ #define gdb_remaining_len(state) ((state)->pkt_len - \ ((state)->pkt_next - (state)->pkt_buf)) /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions Prototypes ****************************************************************************/ /* System functions, supported by all stubs */ static int gdb_getchar(FAR struct gdb_state_s *state); static int gdb_putchar(FAR struct gdb_state_s *state, int ch); /* Packet functions */ static int gdb_send_packet(FAR struct gdb_state_s *state); static int gdb_recv_packet(FAR struct gdb_state_s *state); static int gdb_checksum(FAR const char *buf, size_t len); static int gdb_recv_ack(FAR struct gdb_state_s *state); /* Data format */ static ssize_t gdb_bin2hex(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len); static ssize_t gdb_hex2bin(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len); static ssize_t gdb_bin2bin(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len); /* Packet creation helpers */ static int gdb_send_ok_packet(FAR struct gdb_state_s *state); static int gdb_send_signal_packet(FAR struct gdb_state_s *state, unsigned char signal); static int gdb_send_error_packet(FAR struct gdb_state_s *state, unsigned char error); /* Command functions */ static ssize_t gdb_get_memory(FAR struct gdb_state_s *state, FAR void *buf, size_t buf_len, uintptr_t addr, size_t len, gdb_format_func_t format); static ssize_t gdb_put_memory(FAR struct gdb_state_s *state, FAR const void *buf, size_t buf_len, uintptr_t addr, size_t len, gdb_format_func_t format); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Packet buffer Functions ****************************************************************************/ /**************************************************************************** * Name: gdb_expect_seperator * * Description: * Check if the next byte in the packet is a seperator. * * Input Parameters: * state - The pointer to the GDB state structure. * c - The expected seperator. * * Returned Value: * 0 on success, -EINVAL on error. * ****************************************************************************/ static int gdb_expect_seperator(FAR struct gdb_state_s *state, char c) { if (!state->pkt_next || *state->pkt_next != c) { return -EINVAL; } else { state->pkt_next++; } return 0; } /**************************************************************************** * Name: gdb_expect_integer * * Description: * Parse an integer argument from the packet. * * Input Parameters: * state - The pointer to the GDB state structure. * arg - The parsed integer argument. * * Returned Value: * 0 on success * ****************************************************************************/ static int gdb_expect_integer(FAR struct gdb_state_s *state, FAR uintptr_t *arg) { size_t len = gdb_remaining_len(state); char tmp = state->pkt_next[len]; state->pkt_next[len] = '\0'; *arg = strtoul(state->pkt_next, &state->pkt_next, 16); state->pkt_next[len] = tmp; return 0; } /**************************************************************************** * Name: gdb_expect_addr_lenth * * Description: * Parse an address and length argument from the packet. * * Input Parameters: * state - The pointer to the GDB state structure. * addr - The parsed address. * length - The parsed length. * * Returned Value: * 0 on success, -EINVAL on error. * ****************************************************************************/ static int gdb_expect_addr_lenth(FAR struct gdb_state_s *state, FAR uintptr_t *addr, FAR size_t *length) { int ret; ret = gdb_expect_integer(state, addr); if (ret < 0) { return ret; } ret = gdb_expect_seperator(state, ','); if (ret < 0) { return ret; } return gdb_expect_integer(state, length); } /**************************************************************************** * Packet Functions ****************************************************************************/ /**************************************************************************** * Name: gdb_putchar * * Description: * Put a character to the GDB server. * * Input Parameters: * state - The pointer to the GDB state structure. * ch - The character to be sent. * * Returned Value: * The character sent to the GDB server. * ****************************************************************************/ static int gdb_putchar(FAR struct gdb_state_s *state, int ch) { unsigned char tmp = ch & 0xff; ssize_t ret; ret = state->send(state->priv, &tmp, 1); if (ret < 0) { return ret; } return tmp; } /**************************************************************************** * Name: gdb_getchar * * Description: * Get a character from the GDB server. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * The character read from the GDB server. * ****************************************************************************/ static int gdb_getchar(FAR struct gdb_state_s *state) { unsigned char tmp; ssize_t ret; ret = state->recv(state->priv, &tmp, 1); if (ret < 0) { return ret; } return tmp; } /**************************************************************************** * Name: gdb_send_packet * * Description: * Transmits a packet of data. * Packets are of the form: $# * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if the packet was transmitted and acknowledged. * 1 if the packet was transmitted but not acknowledged. * Negative value on error. * ****************************************************************************/ static int gdb_send_packet(FAR struct gdb_state_s *state) { unsigned char buf[3]; unsigned char csum; int ret; ret = gdb_putchar(state, '$'); /* Send packet start */ if (ret < 0) { return ret; } #ifdef CONFIG_LIB_GDBSTUB_DEBUG { size_t p; GDB_DEBUG("-> "); for (p = 0; p < state->pkt_len; p++) { if (isprint(state->pkt_buf[p])) { GDB_DEBUG("%c", state->pkt_buf[p]); } else { GDB_DEBUG("\\x%02x", state->pkt_buf[p] & 0xff); } } GDB_DEBUG("\n"); } #endif /* Send packet data */ ret = state->send(state->priv, state->pkt_buf, state->pkt_len); if (ret < 0) { return ret; } /* Send the checksum */ buf[0] = '#'; csum = gdb_checksum(state->pkt_buf, state->pkt_len); ret = gdb_bin2hex(buf + 1, sizeof(buf) - 1, &csum, 1); if (ret < 0) { return ret; } ret = state->send(state->priv, buf, sizeof(buf)); if (ret < 0) { return ret; } return gdb_recv_ack(state); } /**************************************************************************** * Name: gdb_recv_packet * * Description: * Receives a packet of data, assuming a 7-bit clean connection. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if the packet was received. * -EIO if the packet was not received. * -EOVERFLOW if the packet was too long. * Negative value on error. * ****************************************************************************/ static int gdb_recv_packet(FAR struct gdb_state_s *state) { unsigned char buf[2]; unsigned char csum; int ret; /* Wait for packet start */ do { while (1) { ret = gdb_getchar(state); if (ret < 0) { return ret; } else if (ret == '$') { /* Detected start of packet. */ break; } } /* Read until checksum */ state->pkt_len = 0; while (1) { ret = gdb_getchar(state); if (ret < 0) /* Error receiving character */ { return ret; } else if (ret == '#') /* End of packet */ { break; } else /* Check for space */ { if (state->pkt_len >= sizeof(state->pkt_buf)) { GDB_DEBUG("packet buffer overflow\n"); return -EOVERFLOW; } /* Store character and update checksum */ state->pkt_buf[state->pkt_len++] = (char)ret; } } } while (state->pkt_len == 0); /* Ignore empty packets */ #ifdef CONFIG_LIB_GDBSTUB_DEBUG { size_t p; GDB_DEBUG("<- "); for (p = 0; p < state->pkt_len; p++) { if (isprint(state->pkt_buf[p])) { GDB_DEBUG("%c", state->pkt_buf[p]); } else { GDB_DEBUG("\\x%02x", state->pkt_buf[p] & 0xff); } } GDB_DEBUG("\n"); } #endif ret = state->recv(state->priv, buf, 2); /* Receive the checksum */ if (ret < 0) { return ret; } ret = gdb_hex2bin(&csum, 1, buf, 2); if (ret < 0) { return ret; } if (csum != gdb_checksum(state->pkt_buf, state->pkt_len)) /* Verify */ { GDB_DEBUG("received packet with bad checksum\n"); gdb_putchar(state, '-'); /* Send packet nack */ return -EIO; } gdb_putchar(state, '+'); /* Send packet ack */ state->pkt_next = state->pkt_buf; return 0; } /**************************************************************************** * Name: gdb_checksum * * Description: * Calculate 8-bit checksum of a buffer. * * Input Parameters: * buf - The buffer to checksum. * * Returned Value: * 8-bit checksum * ****************************************************************************/ static int gdb_checksum(FAR const char *buf, size_t len) { unsigned char csum = 0; while (len--) { csum += *buf++; } return csum; } /**************************************************************************** * Name: gdb_recv_ack * * Description: * Receive a packet acknowledgment. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if an ACK (+) was received * 1 if a NACK (-) was received * -EINVAL if a bad response was received * ****************************************************************************/ static int gdb_recv_ack(FAR struct gdb_state_s *state) { int response; switch (response = gdb_getchar(state)) /* Wait for packet ack */ { case '+': /* Packet acknowledged */ return 0; case '-': /* Packet negative acknowledged */ return 1; default: /* Bad response! */ GDB_DEBUG("received bad packet response: 0x%2x\n", response); return -EINVAL; } } /**************************************************************************** * Data Encoding/Decoding ****************************************************************************/ /**************************************************************************** * Name: gdb_bin2hex * * Description: * Encode data to its hex-value representation in a buffer. * * Input Parameters: * buf - The buffer to encode to. * buf_len - The length of the buffer. * data - The data to encode. * data_len - The length of the data to encode. * * Returned Value: * The number of bytes written to buf on success. * -EOVERFLOW if the buffer is too small. * ****************************************************************************/ static ssize_t gdb_bin2hex(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len) { FAR const char *in = data; FAR char *out = buf; size_t pos; if (buf_len < data_len * 2) { return -EOVERFLOW; /* Buffer too small */ } for (pos = 0; pos < data_len; pos++) { itoa(in[pos] >> 4 & 0xf, out++, 16); itoa(in[pos] & 0xf, out++, 16); } return data_len * 2; } /**************************************************************************** * Name: gdb_hex2bin * * Description: * Decode data from its hex-value representation to a buffer. * * Input Parameters: * buf - The buffer containing the encoded data. * buf_len - The length of the buffer. * data - The buffer to store the decoded data. * data_len - The length of the data to decode. * * Returned Value: * The number of bytes written to data on success. * -EOVERFLOW if the buffer is too small. * Negative value on error. * ****************************************************************************/ static ssize_t gdb_hex2bin(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len) { FAR const char *in = data; FAR char *out = buf; size_t pos; if (buf_len * 2 < data_len) { return -EOVERFLOW; /* Buffer too small */ } for (pos = 0; pos < data_len; pos += 2) { char ch[3] = { in[pos], in[pos + 1], 0 }; out[pos / 2] = strtoul(ch, NULL, 16); /* Decode high nibble */ if (out[pos / 2] == 0 && errno) { GDB_ASSERT(); return -errno; /* Buffer contained junk. */ } } return data_len / 2; } /**************************************************************************** * Name: gdb_bin2bin * * Description: * Decode data from its bin-value representation to a buffer. * * Input Parameters: * buf - The buffer containing the encoded data. * buf_len - The length of the buffer. * data - The buffer to store the decoded data. * data_len - The length of the data to decode. * * Returned Value: * The number of bytes written to data on success. * -EOVERFLOW if the output buffer is too small. * -EINVAL if the input buffer is invalid. * ****************************************************************************/ static ssize_t gdb_bin2bin(FAR void *buf, size_t buf_len, FAR const void *data, size_t data_len) { FAR const char *in = data; FAR char *out = buf; size_t out_pos = 0; size_t in_pos; for (in_pos = 0; in_pos < data_len; in_pos++) { if (out_pos >= buf_len) { GDB_ASSERT(); return -EOVERFLOW; /* Output buffer overflow */ } if (in[in_pos] == '}') /* The next byte is escaped! */ { if (in_pos + 1 >= data_len) { /* There's an escape character, but no escaped character * following the escape character. */ GDB_ASSERT(); return -EINVAL; } in_pos++; out[out_pos++] = in[in_pos] ^ 0x20; } else { out[out_pos++] = in[in_pos]; } } return out_pos; } /**************************************************************************** * Command Functions ****************************************************************************/ /**************************************************************************** * Name: gdb_get_memory * * Description: * Get data from memory. * * Input Parameters: * state - The state structure. * buf - The buffer to store the encoded data. * buf_len - The length of the buffer. * addr - The address to read from. * len - The length of the data to read. * format - The formatting function to use. * * Returned Value: * The number of bytes written to buf on success. * Negative value on error. * ****************************************************************************/ static ssize_t gdb_get_memory(FAR struct gdb_state_s *state, FAR void *buf, size_t buf_len, uintptr_t addr, size_t len, gdb_format_func_t format) { return format(buf, buf_len, (FAR const void *)addr, len); } /**************************************************************************** * Name: gdb_put_memory * * Description: * Put data from buf into memory. * * Input Parameters: * state - The pointer to the GDB state structure. * buf - The buffer containing the encoded data. * buf_len - The length of the buffer. * addr - The address to write to. * len - The length of the data to write. * format - The formatting function to use. * * Returned Value: * The number of bytes written to addr on success. * Negative value on error. * ****************************************************************************/ static ssize_t gdb_put_memory(FAR struct gdb_state_s *state, FAR const void *buf, size_t buf_len, uintptr_t addr, size_t len, gdb_format_func_t format) { return format((FAR void *)addr, len, buf, buf_len); } /**************************************************************************** * Packet Creation Helpers ****************************************************************************/ /**************************************************************************** * Name: gdb_send_ok_packet * * Description: * Send OK packet. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero on success. * Negative value on error. * ****************************************************************************/ static int gdb_send_ok_packet(FAR struct gdb_state_s *state) { state->pkt_buf[0] = 'O'; state->pkt_buf[1] = 'K'; state->pkt_len = 2; return gdb_send_packet(state); } /**************************************************************************** * Name: gdb_send_signal_packet * * Description: * Send a signal packet (S AA). * * Input Parameters: * state - The pointer to the GDB state structure. * signal - The signal to send. * * Returned Value: * Zero on success. * Negative value on error. * ****************************************************************************/ static int gdb_send_signal_packet(FAR struct gdb_state_s *state, unsigned char signal) { int ret; state->pkt_buf[0] = 'S'; ret = gdb_bin2hex(&state->pkt_buf[1], sizeof(state->pkt_buf) - 1, &signal, 1); if (ret < 0) { return ret; } state->pkt_len = 1 + ret; return gdb_send_packet(state); } /**************************************************************************** * Name: gdb_send_error_packet * * Description: * Send a error packet (E AA). * * Input Parameters: * state - The pointer to the GDB state structure. * error - The error to send. * * Returned Value: * Zero on success. * Negative value on error. * ****************************************************************************/ static int gdb_send_error_packet(FAR struct gdb_state_s *state, unsigned char error) { int ret; state->pkt_buf[0] = 'E'; ret = gdb_bin2hex(&state->pkt_buf[1], sizeof(state->pkt_buf) - 1, &error, 1); if (ret < 0) { return ret; } state->pkt_len = 1 + ret; return gdb_send_packet(state); } /**************************************************************************** * Communication Functions ****************************************************************************/ /**************************************************************************** * Name: gdb_get_registers * * Description: * Get the registers of the specified task. * * Input Parameters: * state - The pointer to the GDB state structure. ****************************************************************************/ static void gdb_get_registers(FAR struct gdb_state_s *state) { FAR struct tcb_s *tcb = nxsched_get_tcb(state->pid); FAR uint8_t *reg; int i; reg = (FAR uint8_t *)tcb->xcp.regs; if (state->pid == _SCHED_GETPID()) { if (up_interrupt_context()) { reg = (FAR uint8_t *)CURRENT_REGS; } else { up_saveusercontext(state->running_regs); reg = state->running_regs; } } for (i = 0; i < state->size / sizeof(uintptr_t); i++) { if (g_tcbinfo.reg_off.p[i] == UINT16_MAX) { state->registers[i] = 0; } else { state->registers[i] = *(FAR uintptr_t *)(reg + g_tcbinfo.reg_off.p[i]); } } } /**************************************************************************** * Name: gdb_read_registers * * Description: * Read Registers Command Format: g. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero on success. * Negative value on error. * * Note: Comand Format: g. * Response Format: xxxxxxxxyyyyyyyyy... ****************************************************************************/ static int gdb_read_registers(FAR struct gdb_state_s *state) { int ret; gdb_get_registers(state); ret = gdb_bin2hex(state->pkt_buf, sizeof(state->pkt_buf), state->registers, state->size); if (ret < 0) { return ret; } state->pkt_len = ret; gdb_send_packet(state); return 0; } /**************************************************************************** * Name: gdb_write_registers * * Description: * Write Registers Command Format: G XX... * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero on success. * Negative value on error. * * Note: This function is not really change the register values. * Comand Format: Gxxxxxxxxyyyyyyyyy * Response Format: OK ****************************************************************************/ static int gdb_write_registers(FAR struct gdb_state_s *state) { int ret; ret = gdb_hex2bin(state->registers, sizeof(state->registers), state->pkt_buf + 1, state->pkt_len - 1); if (ret < 0) { return ret; } gdb_send_ok_packet(state); return 0; } /**************************************************************************** * Name: gdb_write_register * * Description: * Write a Register Command Format: P n. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero on success. * Negative value on error. * * Note: Comand Format: Pn. * Response Format: OK ****************************************************************************/ static int gdb_read_register(FAR struct gdb_state_s *state) { uintptr_t addr; int ret; state->pkt_next++; ret = gdb_expect_integer(state, &addr); if (ret < 0) { return ret; } gdb_get_registers(state); if (addr >= state->size) { return 0; } ret = gdb_bin2hex(state->pkt_buf, sizeof(state->pkt_buf), &state->registers[addr], sizeof(state->registers[addr])); if (ret < 0) { return ret; } state->pkt_len = ret; gdb_send_packet(state); return 0; } /**************************************************************************** * Name: gdb_read_memory * * Description: * Read Memory Command Format: m addr,length * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * The number of bytes read if successful. * Negative value on error. * * Note: Comand Format: mAAAAAAAAA,LLLLLLLL * Response Format: XXXXXXXXYYYYYYYYY... ****************************************************************************/ static int gdb_read_memory(FAR struct gdb_state_s *state) { uintptr_t addr; size_t length; int ret; state->pkt_next++; ret = gdb_expect_addr_lenth(state, &addr, &length); if (ret < 0) { return ret; } ret = gdb_get_memory(state, state->pkt_buf, sizeof(state->pkt_buf), addr, length, gdb_bin2hex); if (ret < 0) { return ret; } state->pkt_len = ret; gdb_send_packet(state); return ret; } /**************************************************************************** * Name: gdb_write_memory * * Description: * Write Memory Command Format: M addr,length:XX.. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * The number of bytes written if successful. * Negative value on error. * * Note : Comand Format: MAAAAAAAAA,LLLLLLLL:XXXXXXXXX... * Response Format: OK ****************************************************************************/ static int gdb_write_memory(FAR struct gdb_state_s *state) { uintptr_t addr; size_t length; int ret; state->pkt_next++; ret = gdb_expect_addr_lenth(state, &addr, &length); if (ret < 0) { return ret; } ret = gdb_put_memory(state, state->pkt_next, gdb_remaining_len(state), addr, length, gdb_hex2bin); if (ret < 0) { return ret; } gdb_send_ok_packet(state); return ret; } /**************************************************************************** * Name: gdb_write_bin_memory * * Description: * Write Memory (Binary) Command Format: X addr,length:XX.. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * The number of bytes written if successful. * Negative value on error. * * Note : Comand Format: XAAAAAAAAA,LLLLLLLL:XXXXXXXXX... * Response Format: OK ****************************************************************************/ static int gdb_write_bin_memory(FAR struct gdb_state_s *state) { uintptr_t addr; size_t length; int ret; state->pkt_next++; ret = gdb_expect_addr_lenth(state, &addr, &length); if (ret < 0) { return ret; } ret = gdb_expect_seperator(state, ':'); if (ret < 0) { return ret; } ret = gdb_put_memory(state, state->pkt_next, gdb_remaining_len(state), addr, length, gdb_bin2bin); if (ret < 0) { return ret; } gdb_send_ok_packet(state); return ret; } /**************************************************************************** * Name: gdb_get_thread * * Description: * Get the all thread id. * * Input Parameters: * state - The pointer to the GDB state structure. * ****************************************************************************/ static void gdb_get_thread(FAR struct tcb_s *tcb, FAR void *arg) { FAR struct gdb_state_s *state = arg; int pid = tcb->pid; size_t len; len = sprintf(&state->pkt_buf[state->pkt_len], "%x,", pid); state->pkt_len += len; } /**************************************************************************** * Name: gdb_query * * Description: * The query packet is used by GDB to request information from the stub. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if successful. * Negative value on error. * * Note : Comand Format: qSTRING * STRING:is the query string. ****************************************************************************/ static int gdb_query(FAR struct gdb_state_s *state) { static const char thread_extra_info[] = "ThreadExtraInfo"; static const char fthread_info[] = "fThreadInfo"; static const char sthread_info[] = "sThreadInfo"; static const char r_cmd[] = "Rcmd"; if (memcmp(&state->pkt_buf[1], fthread_info, sizeof(fthread_info) - 1) == 0) { state->pkt_buf[0] = 'm'; state->pkt_len = 1; nxsched_foreach(gdb_get_thread, state); state->pkt_len--; state->pkt_buf[state->pkt_len] = 0; gdb_send_packet(state); } else if (memcmp(&state->pkt_buf[1], sthread_info, sizeof(sthread_info) - 1) == 0) { state->pkt_buf[0] = 'l'; state->pkt_len = 1; gdb_send_packet(state); } else if (memcmp(&state->pkt_buf[1], thread_extra_info, sizeof(thread_extra_info) - 1) == 0) { FAR struct tcb_s *tcb; char thread_state[32]; char thread_info[128]; uintptr_t pid; int ret; state->pkt_next += sizeof(thread_extra_info) + 1; ret = gdb_expect_integer(state, &pid); if (ret < 0) { return ret; } tcb = nxsched_get_tcb(pid); if (tcb == NULL) { return -EINVAL; } nxsched_get_stateinfo(tcb, thread_state, sizeof(thread_state)); #if CONFIG_TASK_NAME_SIZE > 0 snprintf(thread_info, sizeof(thread_info), "Name: %s, State: %s, Priority: %d, Stack: %zu", tcb->name, thread_state, tcb->sched_priority, tcb->adj_stack_size); #else snprintf(thread_info, sizeof(thread_info), "State: %s, Priority: %d, Stack: %zu", thread_state, tcb->sched_priority, tcb->adj_stack_size); #endif ret = gdb_bin2hex(state->pkt_buf, sizeof(state->pkt_buf), thread_info, strlen(thread_info)); state->pkt_len = ret; gdb_send_packet(state); } else if (state->monitor != NULL && memcmp(&state->pkt_buf[1], r_cmd, sizeof(r_cmd) - 1) == 0) { ssize_t len = state->pkt_len - sizeof(r_cmd) - 1; /* skip the 'Rcmd,' */ char cmd[128]; int ret; len = gdb_hex2bin(cmd, len / 2, &state->pkt_buf[sizeof(r_cmd) + 1], len); if (len < 0) { return len; } cmd[len] = '\0'; ret = state->monitor(state, cmd); if (ret < 0) { return ret; } else { gdb_send_ok_packet(state); } } else { return -EPROTONOSUPPORT; } return 0; } /**************************************************************************** * Name: gdb_is_thread_active * * Description: * The is thread active packet is used by GDB to request information from * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if successful. * Negative value on error. * * Note : Comand Format: T * id:is the thread id. * Rsponse Format: OK ****************************************************************************/ static int gdb_is_thread_active(FAR struct gdb_state_s *state) { FAR struct tcb_s *tcb; uintptr_t pid; int ret; state->pkt_next++; ret = gdb_expect_integer(state, &pid); if (ret < 0) { return ret; } tcb = nxsched_get_tcb(pid); if (tcb == NULL) { return -EINVAL; } state->pid = pid; gdb_send_ok_packet(state); return ret; } /**************************************************************************** * Name: gdb_thread_context * * Description: * The thread context packet is used by GDB to request information from * the stub. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if successful. * Negative value on error. * * Note : Comand Format: Hc * Rsponse Format: OK ****************************************************************************/ static int gdb_thread_context(FAR struct gdb_state_s *state) { FAR struct tcb_s *tcb; uintptr_t pid; int ret; state->pkt_next += 2; ret = gdb_expect_integer(state, &pid); if (ret < 0) { return ret; } tcb = nxsched_get_tcb(pid); if (tcb == NULL) { return -EINVAL; } state->pid = pid; gdb_send_ok_packet(state); return 0; } /**************************************************************************** * Name: gdb_send_stop * * Description: * The stop packet is used by GDB to request information from the stub. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * 0 if successful. * Negative value on error. * ****************************************************************************/ static int gdb_send_stop(FAR struct gdb_state_s *state) { return gdb_send_signal_packet(state, 0x00); } /**************************************************************************** * Public Data ****************************************************************************/ extern const struct tcbinfo_s g_tcbinfo; /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: gdb_state_init * * Description: * Initialize the GDB state structure. * * Input Parameters: * send - The pointer to the send function. * recv - The pointer to the receive function. * monitor - The pointer to the monitor_cmd function. * priv - The pointer to the private data. * * Returned Value: * The pointer to the GDB state structure on success. * NULL on failure. * ****************************************************************************/ FAR struct gdb_state_s *gdb_state_init(gdb_send_func_t send, gdb_recv_func_t recv, gdb_monitor_func_t monitor, FAR void *priv) { size_t size = g_tcbinfo.basic_num * sizeof(uintptr_t); FAR struct gdb_state_s *state = lib_zalloc(sizeof(*state) + size); if (state == NULL) { return NULL; } state->size = size; state->send = send; state->recv = recv; state->priv = priv; state->monitor = monitor; return state; } /**************************************************************************** * Name: gdb_state_uninit * * Description: * Uninitialize the GDB state structure. * * Input Parameters: * state - The pointer to the GDB state structure. * ****************************************************************************/ void gdb_state_uninit(FAR struct gdb_state_s *state) { if (state != NULL) { lib_free(state); } } /**************************************************************************** * Name: gdb_console_message * * Description: * Send a message to the GDB console (via O XX... packet). * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero on success. * Negative value on error. * ****************************************************************************/ int gdb_console_message(FAR struct gdb_state_s *state, FAR const char *msg) { ssize_t ret; if (state == NULL || msg == NULL) { return -EINVAL; } state->pkt_buf[0] = 'O'; ret = gdb_bin2hex(&state->pkt_buf[1], sizeof(state->pkt_buf) - 1, msg, strlen(msg)); if (ret < 0) { return ret; } state->pkt_len = 1 + ret; return gdb_send_packet(state); } /**************************************************************************** * Name: gdb_process * * Description: * Main debug loop. Handles commands. * * Input Parameters: * state - The pointer to the GDB state structure. * * Returned Value: * Zero if successful. * Negative value on error. * ****************************************************************************/ int gdb_process(FAR struct gdb_state_s *state) { int ret; while ((ret = gdb_recv_packet(state)) >= 0) { /* Handle one letter commands */ switch (state->pkt_buf[0]) { case '?': /* gdbserial status */ ret = gdb_send_stop(state); break; case 'g': /* Read registers */ ret = gdb_read_registers(state); break; case 'G': /* Write registers */ ret = gdb_write_registers(state); break; case 'p': /* Read one register */ ret = gdb_read_register(state); break; case 'm': /* Read memory */ ret = gdb_read_memory(state); break; case 'M': /* Write memory */ ret = gdb_write_memory(state); break; case 'X': /* Write binary memory */ ret = gdb_write_bin_memory(state); break; case 'q': /* Query command */ ret = gdb_query(state); break; case 'T': /* Query thread */ ret = gdb_is_thread_active(state); break; case 'H': /* Thread related */ ret = gdb_thread_context(state); break; case 'k': /* Kill request */ ret = -ECONNRESET; break; default: ret = -EPROTONOSUPPORT; } if (ret == -EPROTONOSUPPORT) { state->pkt_len = 0; gdb_send_packet(state); } else if (ret == -ECONNRESET) { break; } else if (ret < 0) { gdb_send_error_packet(state, 0x00); } } return ret; }