/* * Copyright (c) 2020 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include LOG_MODULE_REGISTER(gdbstub); #include #include #include #include #include #include #include #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 " */ 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);