123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * Copyright (c) 2020 Intel Corporation.
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <device.h>
- #include <kernel.h>
- #include <logging/log.h>
- LOG_MODULE_REGISTER(gdbstub);
- #include <sys/util.h>
- #include <ctype.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include "gdbstub_backend.h"
- #define GDB_PACKET_SIZE 256
- /* GDB remote serial protocol does not define errors value properly
- * and handle all error packets as the same the code error is not
- * used. There are informal values used by others gdbstub
- * implementation, like qemu. Lets use the same here.
- */
- #define GDB_ERROR_GENERAL "E01"
- #define GDB_ERROR_MEMORY "E14"
- #define GDB_ERROR_OVERFLOW "E22"
- /**
- * Add preamble and termination to the given data.
- *
- * It returns 0 if the packet was acknowledge, -1 otherwise.
- */
- static int gdb_send_packet(const uint8_t *data, size_t len)
- {
- uint8_t buf[2];
- uint8_t checksum = 0;
- /* Send packet start */
- z_gdb_putchar('$');
- /* Send packet data and calculate checksum */
- while (len-- > 0) {
- checksum += *data;
- z_gdb_putchar(*data++);
- }
- /* Send the checksum */
- z_gdb_putchar('#');
- if (bin2hex(&checksum, 1, buf, sizeof(buf)) == 0) {
- return -1;
- }
- z_gdb_putchar(buf[0]);
- z_gdb_putchar(buf[1]);
- if (z_gdb_getchar() == '+') {
- return 0;
- }
- /* Just got an invalid response */
- return -1;
- }
- /**
- * Receives a packet
- *
- * Return 0 in case of success, otherwise -1
- */
- static int gdb_get_packet(uint8_t *buf, size_t buf_len, size_t *len)
- {
- uint8_t ch = '0';
- uint8_t expected_checksum, checksum = 0;
- uint8_t checksum_buf[2];
- /* Wait for packet start */
- checksum = 0;
- /* wait for the start character, ignore the rest */
- while (ch != '$') {
- ch = z_gdb_getchar();
- }
- *len = 0;
- /* Read until receive # or the end of the buffer */
- while (*len < (buf_len - 1)) {
- ch = z_gdb_getchar();
- if (ch == '#') {
- break;
- }
- checksum += ch;
- buf[*len] = ch;
- (*len)++;
- }
- buf[*len] = '\0';
- /* Get checksum now */
- checksum_buf[0] = z_gdb_getchar();
- checksum_buf[1] = z_gdb_getchar();
- if (hex2bin(checksum_buf, 2, &expected_checksum, 1) == 0) {
- return -1;
- }
- /* Verify checksum */
- if (checksum != expected_checksum) {
- LOG_DBG("Bad checksum. Got 0x%x but was expecting: 0x%x",
- checksum, expected_checksum);
- /* NACK packet */
- z_gdb_putchar('-');
- return -1;
- }
- /* ACK packet */
- z_gdb_putchar('+');
- return 0;
- }
- /**
- * Read data from a given memory.
- *
- * Return 0 in case of success, otherwise -1
- */
- static int gdb_mem_read(uint8_t *buf, size_t buf_len,
- uintptr_t addr, size_t len)
- {
- uint8_t data;
- size_t pos, count = 0;
- if (len > buf_len) {
- return -1;
- }
- /* Read from system memory */
- for (pos = 0; pos < len; pos++) {
- data = *(uint8_t *)(addr + pos);
- count += bin2hex(&data, 1, buf + count, buf_len - count);
- }
- return count;
- }
- /**
- * Write data in a given memory.
- *
- * Return 0 in case of success, otherwise -1
- */
- static int gdb_mem_write(const uint8_t *buf, uintptr_t addr,
- size_t len)
- {
- uint8_t data;
- while (len > 0) {
- size_t ret = hex2bin(buf, 2, &data, sizeof(data));
- if (ret == 0) {
- return -1;
- }
- *(uint8_t *)addr = data;
- addr++;
- buf += 2;
- len--;
- }
- return 0;
- }
- /**
- * Send a exception packet "T <value>"
- */
- static int gdb_send_exception(uint8_t *buf, size_t len, uint8_t exception)
- {
- size_t size;
- *buf = 'T';
- size = bin2hex(&exception, 1, buf + 1, len - 1);
- if (size == 0) {
- return -1;
- }
- /* Related to 'T' */
- size++;
- return gdb_send_packet(buf, size);
- }
- /**
- * Synchronously communicate with gdb on the host
- */
- int z_gdb_main_loop(struct gdb_ctx *ctx, bool start)
- {
- uint8_t buf[GDB_PACKET_SIZE];
- enum loop_state {
- RECEIVING,
- CONTINUE,
- FAILED
- } state;
- state = RECEIVING;
- if (start == false) {
- gdb_send_exception(buf, sizeof(buf), ctx->exception);
- }
- #define CHECK_FAILURE(condition) \
- { \
- if ((condition)) { \
- state = FAILED; \
- break; \
- } \
- }
- #define CHECK_SYMBOL(c) \
- { \
- CHECK_FAILURE(ptr == NULL || *ptr != (c)); \
- ptr++; \
- }
- #define CHECK_INT(arg) \
- { \
- arg = strtol((const char *)ptr, (char **)&ptr, 16); \
- CHECK_FAILURE(ptr == NULL); \
- }
- while (state == RECEIVING) {
- uint8_t *ptr;
- size_t data_len, pkt_len;
- uintptr_t addr;
- int ret;
- ret = gdb_get_packet(buf, sizeof(buf), &pkt_len);
- CHECK_FAILURE(ret == -1);
- if (pkt_len == 0) {
- continue;
- }
- ptr = buf;
- switch (*ptr++) {
- /**
- * Read from the memory
- * Format: m addr,length
- */
- case 'm':
- CHECK_INT(addr);
- CHECK_SYMBOL(',');
- CHECK_INT(data_len);
- /* Read Memory */
- /*
- * GDB ask the guest to read parameters when
- * the user request backtrace. If the
- * parameter is a NULL pointer this will cause
- * a fault. Just send a packet informing that
- * this address is invalid
- */
- if (addr == 0L) {
- gdb_send_packet(GDB_ERROR_MEMORY, 3);
- break;
- }
- ret = gdb_mem_read(buf, sizeof(buf), addr, data_len);
- CHECK_FAILURE(ret == -1);
- gdb_send_packet(buf, ret);
- break;
- /**
- * Write to memory
- * Format: M addr,length:val
- */
- case 'M':
- CHECK_INT(addr);
- CHECK_SYMBOL(',');
- CHECK_INT(data_len);
- CHECK_SYMBOL(':');
- if (addr == 0L) {
- gdb_send_packet(GDB_ERROR_MEMORY, 3);
- break;
- }
- /* Write Memory */
- pkt_len = gdb_mem_write(ptr, addr, data_len);
- CHECK_FAILURE(pkt_len == -1);
- gdb_send_packet("OK", 2);
- break;
- /*
- * Continue ignoring the optional address
- * Format: c addr
- */
- case 'c':
- arch_gdb_continue();
- state = CONTINUE;
- break;
- /*
- * Step one instruction ignoring the optional address
- * s addr..addr
- */
- case 's':
- arch_gdb_step();
- state = CONTINUE;
- break;
- /*
- * Read all registers
- * Format: g
- */
- case 'g':
- pkt_len = bin2hex((const uint8_t *)&(ctx->registers),
- sizeof(ctx->registers), buf, sizeof(buf));
- CHECK_FAILURE(pkt_len == 0);
- gdb_send_packet(buf, pkt_len);
- break;
- /**
- * Write the value of the CPU registers
- * Fromat: G XX...
- */
- case 'G':
- pkt_len = hex2bin(ptr, pkt_len - 1,
- (uint8_t *)&(ctx->registers),
- sizeof(ctx->registers));
- CHECK_FAILURE(pkt_len == 0);
- gdb_send_packet("OK", 2);
- break;
- /**
- * Read the value of a register
- * Format: p n
- */
- case 'p':
- CHECK_INT(addr);
- CHECK_FAILURE(addr >= ARCH_GDB_NUM_REGISTERS);
- /* Read Register */
- pkt_len = bin2hex(
- (const uint8_t *)&(ctx->registers[addr]),
- sizeof(ctx->registers[addr]),
- buf, sizeof(buf));
- CHECK_FAILURE(pkt_len == 0);
- gdb_send_packet(buf, pkt_len);
- break;
- /**
- * Write data into a specific register
- * Format: P register=value
- */
- case 'P':
- CHECK_INT(addr);
- CHECK_SYMBOL('=');
- /*
- * GDB requires orig_eax that seems to be
- * Linux specific. Unfortunately if we just
- * return "E01" gdb will stop. So, we just
- * send "OK" and ignore it.
- */
- if (addr < ARCH_GDB_NUM_REGISTERS) {
- pkt_len = hex2bin(ptr, strlen(ptr),
- (uint8_t *)&(ctx->registers[addr]),
- sizeof(ctx->registers[addr]));
- CHECK_FAILURE(pkt_len == 0);
- }
- gdb_send_packet("OK", 2);
- break;
- /* What cause the pause */
- case '?':
- gdb_send_exception(buf, sizeof(buf),
- ctx->exception);
- break;
- /*
- * Not supported action
- */
- default:
- gdb_send_packet(NULL, 0);
- break;
- }
- }
- if (state == FAILED) {
- gdb_send_packet(GDB_ERROR_GENERAL, 3);
- return -1;
- }
- #undef CHECK_FAILURE
- #undef CHECK_INT
- #undef CHECK_SYMBOL
- return 0;
- }
- int gdb_init(const struct device *arg)
- {
- ARG_UNUSED(arg);
- if (z_gdb_backend_init() == -1) {
- LOG_ERR("Could not initialize gdbstub backend.");
- return -1;
- }
- arch_gdb_init();
- return 0;
- }
- SYS_INIT(gdb_init, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|