diff --git a/include/debug/gdbstub.h b/include/debug/gdbstub.h index eecb43af049..d809d113078 100644 --- a/include/debug/gdbstub.h +++ b/include/debug/gdbstub.h @@ -15,6 +15,49 @@ #define GDB_EXCEPTION_INVALID_MEMORY 11UL #define GDB_EXCEPTION_OVERFLOW 16UL +/* Access permissions for memory regions */ +#define GDB_MEM_REGION_NO_ACCESS 0UL +#define GDB_MEM_REGION_READ BIT(0) +#define GDB_MEM_REGION_WRITE BIT(1) + +#define GDB_MEM_REGION_RO \ + (GDB_MEM_REGION_READ) + +#define GDB_MEM_REGION_RW \ + (GDB_MEM_REGION_READ | GDB_MEM_REGION_WRITE) + +/** Describe one memory region */ +struct gdb_mem_region { + /** Start address of a memory region */ + uintptr_t start; + + /** End address of a memory region */ + uintptr_t end; + + /** Memory region attributes */ + uint16_t attributes; + + /** Read/write alignment, 0 if using default alignment */ + uint8_t alignment; +}; + +/** + * Memory region descriptions used for GDB memory access. + * + * This array specifies which region of memory GDB can access + * with read/write attribites. This is used to restrict + * memory read/write in GDB stub to memory that can be + * legally accessed without resulting in memory faults. + */ +extern const struct gdb_mem_region gdb_mem_region_array[]; + +/** + * Number of Memory Regions. + * + * Number of elements in gdb_mem_region_array[]; + */ +extern const size_t gdb_mem_num_regions; + /** * @brief Convert a binary array into string representation. * @@ -31,4 +74,31 @@ size_t gdb_bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen); + +/** + * @brief Check if a memory block can be read. + * + * This checks if the specified memory block can be read. + * + * @param[in] addr Starting address of the memory block + * @param[in] len Size of memory block + * @param[out] align Read alignment of region + * + * @return True if memory block can be read, false otherwise. + */ +bool gdb_mem_can_read(const uintptr_t addr, const size_t len, uint8_t *align); + +/** + * @brief Check if a memory block can be written into. + * + * This checks if the specified memory block can be written into. + * + * @param[in] addr Starting address of the memory block + * @param[in] len Size of memory block + * @param[out] align Write alignment of region + * + * @return True if GDB stub can write to the block, false otherwise. + */ +bool gdb_mem_can_write(const uintptr_t addr, const size_t len, uint8_t *align); + #endif diff --git a/subsys/debug/gdbstub.c b/subsys/debug/gdbstub.c index b2ae55c656b..6beab61eb75 100644 --- a/subsys/debug/gdbstub.c +++ b/subsys/debug/gdbstub.c @@ -17,8 +17,11 @@ LOG_MODULE_REGISTER(gdbstub); #include #include #include +#include #include +#include +#include #include "gdbstub_backend.h" /* +1 is for the NULL character added during receive */ @@ -35,6 +38,107 @@ LOG_MODULE_REGISTER(gdbstub); static bool not_first_start; +/* Empty memory region array */ +__weak const struct gdb_mem_region gdb_mem_region_array[0]; + +/* Number of memory regions, default to 0 */ +__weak const size_t gdb_mem_num_regions; + +/** + * Given a starting address and length of a memory block, find a memory + * region descriptor from the memory region array where the memory block + * fits inside the memory region. + * + * @param addr Starting address of the memory block + * @param len Length of the memory block + * + * @return Pointer to the memory region description if found. + * NULL if not found. + */ +static inline const +struct gdb_mem_region *find_memory_region(const uintptr_t addr, const size_t len) +{ + const struct gdb_mem_region *r, *ret = NULL; + unsigned int idx; + + for (idx = 0; idx < gdb_mem_num_regions; idx++) { + r = &gdb_mem_region_array[idx]; + + if ((addr >= r->start) && + (addr < r->end) && + ((addr + len) >= r->start) && + ((addr + len) < r->end)) { + ret = r; + break; + } + } + + return ret; +} + +bool gdb_mem_can_read(const uintptr_t addr, const size_t len, uint8_t *align) +{ + bool ret = false; + const struct gdb_mem_region *r; + + if (gdb_mem_num_regions == 0) { + /* + * No region is defined. + * Assume memory access is not restricted, and there is + * no alignment requirement. + */ + *align = 1; + ret = true; + } else { + r = find_memory_region(addr, len); + if (r != NULL) { + if ((r->attributes & GDB_MEM_REGION_READ) == + GDB_MEM_REGION_READ) { + if (r->alignment > 0) { + *align = r->alignment; + } else { + *align = 1; + } + ret = true; + } + } + } + + return ret; +} + +bool gdb_mem_can_write(const uintptr_t addr, const size_t len, uint8_t *align) +{ + bool ret = false; + const struct gdb_mem_region *r; + + if (gdb_mem_num_regions == 0) { + /* + * No region is defined. + * Assume memory access is not restricted, and there is + * no alignment requirement. + */ + *align = 1; + ret = true; + } else { + r = find_memory_region(addr, len); + if (r != NULL) { + if ((r->attributes & GDB_MEM_REGION_WRITE) == + GDB_MEM_REGION_WRITE) { + if (r->alignment > 0) { + *align = r->alignment; + } else { + *align = 1; + } + + ret = true; + } + } + } + + return ret; +} + size_t gdb_bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen) { if ((hexlen + 1) < buflen * 2) { @@ -159,18 +263,24 @@ static int gdb_get_packet(uint8_t *buf, size_t buf_len, size_t *len) } /** - * Read data from a given memory. + * Read data from a given memory address and length. * - * Return 0 in case of success, otherwise -1 + * @return Number of bytes read from memory, otherwise -1 */ static int gdb_mem_read(uint8_t *buf, size_t buf_len, uintptr_t addr, size_t len) { - uint8_t data; + uint8_t data, align; size_t pos, count = 0; if (len > buf_len) { - return -1; + count = -1; + goto out; + } + + if (!gdb_mem_can_read(addr, len, &align)) { + count = -1; + goto out; } /* Read from system memory */ @@ -179,24 +289,32 @@ static int gdb_mem_read(uint8_t *buf, size_t buf_len, count += gdb_bin2hex(&data, 1, buf + count, buf_len - count); } +out: return count; } /** - * Write data in a given memory. + * Write data to a given memory address and length. * - * Return 0 in case of success, otherwise -1 + * @return 0 if successful, otherwise -1 */ static int gdb_mem_write(const uint8_t *buf, uintptr_t addr, size_t len) { - uint8_t data; + uint8_t data, align; + int ret = 0; + + if (!gdb_mem_can_write(addr, len, &align)) { + ret = -1; + goto out; + } while (len > 0) { size_t ret = hex2bin(buf, 2, &data, sizeof(data)); if (ret == 0) { - return -1; + ret = -1; + goto out; } *(uint8_t *)addr = data; @@ -206,7 +324,10 @@ static int gdb_mem_write(const uint8_t *buf, uintptr_t addr, len--; } - return 0; + ret = 0; + +out: + return ret; } /**