* Copyright (c) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
*
* UniProton is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* See the Mulan PSL v2 for more details.
* Create: 2023-09-14
* Description: gdbstub通用部分,包括流程控制,软件断点管理等
*/
#include <stddef.h>
#include <errno.h>
#include <securec.h>
#include "prt_typedef.h"
#include "prt_gdbstub_ext.h"
#include "prt_notifier.h"
#include "ringbuffer.h"
#include "rsp_utils.h"
#include "arch_interface.h"
#include "gdbstub_common.h"
#include "prt_buildef.h"
#include "prt_cpu_external.h"
#include "prt_atomic.h"
#define GDB_EXCEPTION_BREAKPOINT 5
#define GDB_PACKET_SIZE 2048
#define GDB_ENO_NOT_SUPPORT 2
* 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_INVAL "E22"
#define CHECK_ERROR(condition) { \
if ((condition)) { \
return -1; \
} \
}
#define CHECK_CHAR(c) \
{ \
CHECK_ERROR(ptr == NULL || *ptr != (c)); \
ptr++; \
}
enum LoopState {
RECEIVING,
EXIT,
} state;
#define MAX_HANDLER_NUM 128
static STUB_DATA char g_notFirstStart;
static STUB_DATA char g_gdbActive;
static STUB_DATA volatile char g_exitDbg;
static STUB_DATA volatile int g_coreId;
static STUB_DATA volatile int g_prevCoreId;
static STUB_DATA int g_thIdx;
static STUB_DATA int g_firstCoreId = -1;
static STUB_DATA volatile U32 g_onlineBitmap = 0;
#ifdef OS_OPTION_SMP
static STUB_DATA struct Atomic32 g_onlineCores = {0};
static STUB_DATA volatile uintptr_t g_initLock = OS_SPINLOCK_UNLOCK;
static STUB_DATA volatile int g_excState[OS_MAX_CORE_NUM];
static STUB_DATA volatile uintptr_t g_dbgMasterLock = OS_SPINLOCK_UNLOCK;
static STUB_DATA volatile uintptr_t g_dbgSlaveLock = OS_SPINLOCK_UNLOCK;
static STUB_DATA volatile uintptr_t g_dbgLock = OS_SPINLOCK_UNLOCK;
static STUB_DATA struct Atomic32 g_dbgMasters = {0};
static STUB_DATA struct Atomic32 g_dbgSlaves = {0};
static STUB_DATA struct Atomic32 g_ssCoreId = {-1};
static STUB_DATA volatile int g_ssFlg = 0;
static STUB_TEXT void atomic_inc(struct Atomic32 *v)
{
OsAtomic32Add(1, v);
}
static STUB_TEXT void atomic_dec(struct Atomic32 *v)
{
OsAtomic32Add(-1, v);
}
static STUB_TEXT void atomic_set(struct Atomic32 *v, int i)
{
v->counter = i;
}
static STUB_TEXT int atomic_read(struct Atomic32 *v)
{
return v->counter;
}
static STUB_TEXT bool raw_spin_trylock(volatile uintptr_t *lock)
{
return OsSplTryLock(lock);
}
static STUB_TEXT void raw_spin_lock(volatile uintptr_t *lock)
{
OsSplLock(lock);
}
static STUB_TEXT void raw_spin_unlock(volatile uintptr_t *lock)
{
OsSplUnlock(lock);
}
static STUB_TEXT bool raw_spin_is_locked(volatile uintptr_t *lock)
{
return *lock == OS_SPINLOCK_LOCK;
}
#endif
static STUB_TEXT bool OsCpuOnlineCheckMask(U8 cpu_id)
{
return (g_onlineBitmap & (1U << cpu_id)) != 0;
}
* Holds information about breakpoints.
*/
static STUB_DATA struct GdbBkpt g_breaks[GDB_MAX_BREAKPOINTS] = {
[0 ... GDB_MAX_BREAKPOINTS - 1] = { .state = BP_UNDEFINED }
};
static STUB_DATA U8 g_serialBuf[GDB_PACKET_SIZE];
static STUB_TEXT int HexCh2Bin(unsigned char ch)
{
unsigned char cu = ch & 0xdf;
return -1 +
((ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8) +
((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8);
}
* While we find nice hex chars, build a long val.
* Return number of chars processed.
*/
static STUB_TEXT int OsGdbHex2U64(char **ptr, U64 *val, int maxlen)
{
int hex;
int num = 0;
int negate = 0;
if (ptr == NULL || *ptr == NULL || val == NULL || maxlen < 0) {
return 0;
}
*val = 0;
if (**ptr == '-') {
negate = 1;
(*ptr)++;
}
while (**ptr && num < maxlen) {
hex = HexCh2Bin(**ptr);
if (hex < 0)
break;
*val = (*val << 4) | hex;
num++;
(*ptr)++;
}
if (negate)
*val = -*val;
return num;
}
#define CHECK_HEX(arg) do { \
U64 v = 0; \
OsGdbHex2U64((char **)&ptr, &v, 2 * sizeof(v)); \
CHECK_ERROR(ptr == NULL); \
arg =( __typeof__(arg)) v; \
} while(0)
#define CHECK_HEXS(arg, cnt) do { \
U64 v = 0; \
cnt = OsGdbHex2U64((char **)&ptr, &v, 2 * sizeof(v)); \
CHECK_ERROR(ptr == NULL); \
arg =( __typeof__(arg)) v; \
} while(0)
extern const char __os_section_start[];
extern const char __os_section_end[];
extern const char __os_stub_data_start[];
extern const char __os_stub_data_end[];
extern const char __os_stub_text_start[];
extern const char __os_stub_text_end[];
#define MAX_REGIONS 3
static STUB_DATA struct GdbMemRegion g_regions[MAX_REGIONS];
STUB_TEXT int OsGdbConfigInitMemRegions(void)
{
g_regions[0].start = (uintptr_t)__os_section_start;
g_regions[0].end = (uintptr_t)__os_section_end;
g_regions[0].attributes = GDB_MEM_REGION_RW;
g_regions[1].start = (uintptr_t)__os_stub_text_start;
g_regions[1].end = (uintptr_t)__os_stub_text_end;
g_regions[1].attributes = GDB_MEM_REGION_NO_BKPT;
g_regions[2].start = (uintptr_t)__os_stub_data_start;
g_regions[2].end = (uintptr_t)__os_stub_data_start;
g_regions[2].attributes = GDB_MEM_REGION_NO_BKPT;
}
static STUB_TEXT int GdbAddrCheck(uintptr_t addr, int len, int attr)
{
for (int i = 0; i < MAX_REGIONS; i++) {
if (addr >= g_regions[i].start &&
addr + len < g_regions[i].end &&
(g_regions[i].attributes & attr) == attr) {
return 0;
}
}
return -EINVAL;
}
static STUB_TEXT int GdbInvalidReadAddr(uintptr_t addr, int len)
{
return GdbAddrCheck(addr, len, GDB_MEM_REGION_READ);
}
static STUB_TEXT int GdbInvalidWriteAddr(uintptr_t addr, int len)
{
return GdbAddrCheck(addr, len, GDB_MEM_REGION_WRITE);
}
static STUB_TEXT int GdbInvalidBkptAddr(uintptr_t addr)
{
return !GdbAddrCheck(addr, BREAK_INSTR_SIZE, GDB_MEM_REGION_NO_BKPT);
}
* Some architectures need cache flushes when we set/clear a
* breakpoint:
*/
void __weak STUB_TEXT GdbFlushSwBkptAddr(uintptr_t addr)
{
(void)(addr);
}
* SW breakpoint management:
*/
static STUB_TEXT int GdbSetSwBkpt(uintptr_t addr)
{
int breakno = -1;
int i;
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if (g_breaks[i].state == BP_SET &&
g_breaks[i].addr == addr)
return -EEXIST;
}
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if (g_breaks[i].state == BP_REMOVED &&
g_breaks[i].addr == addr) {
breakno = i;
break;
}
}
if (breakno == -1) {
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if (g_breaks[i].state == BP_UNDEFINED) {
breakno = i;
break;
}
}
}
if (breakno == -1)
return -E2BIG;
g_breaks[breakno].state = BP_SET;
g_breaks[breakno].type = BP_BREAKPOINT;
g_breaks[breakno].addr = addr;
return 0;
}
static STUB_TEXT int GdbActivateSwBkpts(void)
{
int error;
int ret = 0;
int i;
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if (g_breaks[i].state != BP_SET)
continue;
error = OsGdbArchSetSwBkpt(&g_breaks[i]);
if (error) {
ret++;
continue;
}
GdbFlushSwBkptAddr(g_breaks[i].addr);
g_breaks[i].state = BP_ACTIVE;
}
return ret;
}
static STUB_TEXT int GdbDeactivateSwBkpts(void)
{
int ret = 0;
int i;
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if (g_breaks[i].state != BP_ACTIVE)
continue;
ret = OsGdbArchRemoveSwBkpt(&g_breaks[i]);
GdbFlushSwBkptAddr(g_breaks[i].addr);
g_breaks[i].state = BP_SET;
}
return ret;
}
static STUB_TEXT int GdbRemoveSwBkpt(uintptr_t addr)
{
int i;
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
if ((g_breaks[i].state == BP_SET) &&
(g_breaks[i].addr == addr)) {
g_breaks[i].state = BP_REMOVED;
return 0;
}
}
return -ENOENT;
}
static STUB_TEXT int GdbResetBkpts(void)
{
int i;
for (i = 0; i < GDB_MAX_BREAKPOINTS; i++) {
g_breaks[i].state = BP_UNDEFINED;
}
return 0;
}
INLINE int GdbNotSupportBkptType(U8 type) {
return type != BP_BREAKPOINT &&
type != BP_WRITE_WATCHPOINT &&
type != BP_ACCESS_WATCHPOINT;
}
static STUB_TEXT int GdbAddBkpt(U8 type, uintptr_t addr, U32 kind)
{
if (GdbNotSupportBkptType(type)) {
return -GDB_ENO_NOT_SUPPORT;
}
if (GdbInvalidBkptAddr(addr)) {
return -EINVAL;
}
if (type == BP_BREAKPOINT) {
return GdbSetSwBkpt(addr);
}
return OsGdbArchSetHwBkpt(addr, kind, type);
}
static STUB_TEXT int GdbRemoveBkpt(U8 type, uintptr_t addr, U32 kind)
{
if (GdbNotSupportBkptType(type)) {
return -GDB_ENO_NOT_SUPPORT;
}
if (GdbInvalidBkptAddr(addr)) {
return -EINVAL;
}
if (type == BP_BREAKPOINT) {
return GdbRemoveSwBkpt(addr);
}
return OsGdbArchRemoveHwBkpt(addr, kind, type);
}
static STUB_TEXT int GdbRawMemRead(U8 *buf, int buf_len, uintptr_t addr, int len)
{
U8 data;
int count = 0;
int pos;
for (pos = 0; pos < len; pos++) {
data = *(U8 *)(addr + pos);
count += OsGdbBin2Hex(&data, sizeof(data), buf + count, buf_len - count);
}
return count;
}
static STUB_TEXT int GdbRawMemWrite(const U8 *buf, uintptr_t addr, int len)
{
U8 data;
int count = 0;
while (len > 0) {
int cnt = OsGdbHex2Bin(buf, 2, &data, sizeof(data));
if (cnt == 0) {
return -1;
}
*(U8 *)addr = data;
count += cnt;
addr++;
buf += 2;
len--;
}
return count;
}
* Read from the memory
* Format: m addr,length
*/
static STUB_TEXT int GdbCmdMemRead(U8 *ptr, int bufLen)
{
(void)bufLen;
int len;
uintptr_t addr;
int ret;
CHECK_HEX(addr);
CHECK_CHAR(',');
CHECK_HEX(len);
* 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 (GdbInvalidReadAddr(addr, len)) {
OsGdbSendPacket(GDB_ERROR_MEMORY, 3);
return 0;
}
ret = GdbRawMemRead(g_serialBuf, sizeof(g_serialBuf), addr, len);
CHECK_ERROR(!ret);
OsGdbSendPacket(g_serialBuf, ret);
return ret;
}
* Write to memory
* Format: M addr,length:val
*/
static STUB_TEXT int GdbCmdMemWrite(U8 *ptr, int bufLen)
{
(void)bufLen;
int len;
uintptr_t addr;
CHECK_HEX(addr);
CHECK_CHAR(',');
CHECK_HEX(len);
CHECK_CHAR(':');
if (GdbInvalidWriteAddr(addr, len)) {
OsGdbSendPacket(GDB_ERROR_MEMORY, 3);
return 0;
}
len = GdbRawMemWrite(ptr, addr, len);
CHECK_ERROR(len < 0);
OsGdbSendPacket("OK", 2);
return 0;
}
* Breakpoints
*/
static STUB_TEXT int GdbCmdBreak(U8 *ptr, int len)
{
(void)len;
U32 kind;
uintptr_t addr;
U8 type;
int ret = 0;
CHECK_HEX(type);
CHECK_CHAR(',');
CHECK_HEX(addr);
CHECK_CHAR(',');
CHECK_HEX(kind);
if (g_serialBuf[0] == 'Z') {
ret = GdbAddBkpt(type, addr, kind);
} else if (g_serialBuf[0] == 'z') {
ret = GdbRemoveBkpt(type, addr, kind);
}
if (ret == -GDB_ENO_NOT_SUPPORT) {
OsGdbSendPacket(NULL, 0);
} else if (ret < 0) {
OsGdbSendPacket(GDB_ERROR_INVAL, 3);
} else {
OsGdbSendPacket("OK", 2);
}
return 0;
}
* Read register
* Format: p N
*/
static STUB_TEXT int GdbCmdReadReg(U8 *ptr, int len)
{
(void)len;
U32 regno;
int ret;
CHECK_HEX(regno);
ret = OsGdbArchReadReg(g_coreId, regno, g_serialBuf, sizeof(g_serialBuf));
CHECK_ERROR(ret == 0)
OsGdbSendPacket(g_serialBuf, ret);
return 0;
}
* Write the value of the CPU register
* Format: P N...=XXX...
*/
static STUB_TEXT int GdbCmdWriteReg(U8 *ptr, int len)
{
U32 regno;
int ret;
int cnt;
CHECK_HEXS(regno, cnt);
CHECK_CHAR('=');
ret = OsGdbArchWriteReg(g_coreId, regno, ptr, len - cnt - 1);
CHECK_ERROR(ret < 0)
OsGdbSendPacket("OK", 2);
return 0;
}
static STUB_TEXT int GdbSendStopReply(U8 *ptr, int len)
{
(void)ptr;
(void)len;
uintptr_t addr;
unsigned type;
U32 cid = OsGdbGetCoreID();
int ret = 0;
int stopReason = OsGdbGetStopReason();
if (OsGdbArchHitHwBkpt(&addr, &type)) {
const char *typeStr = GetWatchTypeStr(type);
if (typeStr == NULL) {
ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "T%02xthread:%02x;", stopReason, cid);
} else {
ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "T%02x%s:%x;thread:%02x;",
stopReason, typeStr, addr, cid);
}
} else {
ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "T%02xthread:%02x;", stopReason, cid);
}
if (ret < 0) {
OsGdbSendException(g_serialBuf, sizeof(g_serialBuf), stopReason);
return 0;
}
OsGdbSendPacket(g_serialBuf, ret);
return 0;
}
static STUB_TEXT int GdbCmdQuery(U8 *ptr, int len)
{
(void)len;
int ret;
int maxCoreNum;
switch (*ptr) {
case 's':
case 'f':
if (memcmp(&ptr[1], "ThreadInfo", 10)) {
break;
}
if (*ptr == 'f') {
g_thIdx = 0;
}
#ifdef OS_OPTION_SMP
maxCoreNum = atomic_read(&g_onlineCores);
#else
maxCoreNum = 1;
#endif
if (g_thIdx < maxCoreNum) {
ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "m%x", g_firstCoreId + g_thIdx);
g_thIdx++;
OsGdbSendPacket(g_serialBuf, ret);
} else {
OsGdbSendPacket("l", 1);
}
break;
case 'C':
ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "QC%x;", OsGdbGetCoreID());
OsGdbSendPacket(g_serialBuf, ret);
break;
default:
OsGdbSendPacket(NULL, 0);
break;
}
return 0;
}
static STUB_TEXT int GdbCmdSetThread(U8 *ptr, int len)
{
(void)len;
int coreId = 0;
U8 type = *ptr;
ptr++;
if (type != 'g' && type != 'c') {
return -1;
}
CHECK_HEX(coreId);
if (coreId == -1) {
g_coreId = g_firstCoreId;
return 0;
}
if (coreId < g_firstCoreId || coreId > MAX_CORE_NUM) {
return -1;
}
if (!OsCpuOnlineCheckMask(coreId)) {
return -1;
}
g_coreId = coreId;
return 0;
}
static STUB_TEXT int GdbCmdThreadAlive(U8 *ptr, int len)
{
(void)len;
int coreId = 0;
CHECK_HEX(coreId);
if (coreId < g_firstCoreId || coreId > MAX_CORE_NUM) {
OsGdbSendPacket(GDB_ERROR_INVAL, 3);
return 0;
}
if (OsCpuOnlineCheckMask(coreId)) {
OsGdbSendPacket("OK", 2);
return 0;
}
OsGdbSendPacket(GDB_ERROR_INVAL, 3);
return 0;
}
* Continue ignoring the optional address
* Format: c addr
*/
static STUB_TEXT int GdbCmdContinue(U8 *ptr, int len)
{
(void)ptr;
(void)len;
if (OsGdbArchContinue(g_coreId) != 0) {
OsGdbSendPacket(GDB_ERROR_INVAL, 3);
return 0;
}
#ifdef OS_OPTION_SMP
g_ssFlg = 0;
atomic_set(&g_ssCoreId, -1);
if (g_prevCoreId != g_coreId) {
OsGdbArchContinue(g_prevCoreId);
}
smp_mb();
#endif
OsGdbSendPacket("OK", 2);
state = EXIT;
return 0;
}
* Step one instruction ignoring the optional address
* s addr..addr
*/
static STUB_TEXT int GdbCmdStep(U8 *ptr, int len)
{
(void)ptr;
(void)len;
if (OsGdbArchStep(g_coreId) != 0) {
return 0;
}
#ifdef OS_OPTION_SMP
g_ssFlg = 1;
atomic_set(&g_ssCoreId, g_coreId);
if (g_prevCoreId != g_coreId) {
OsGdbArchContinue(g_prevCoreId);
g_ssFlg = 0;
}
smp_mb();
#endif
state = EXIT;
return 0;
}
static STUB_TEXT int GdbCmdReadAllRegs(U8 *ptr, int len)
{
(void)ptr;
(void)len;
int ret = OsGdbArchReadAllRegs(g_coreId, g_serialBuf, sizeof(g_serialBuf));
CHECK_ERROR(ret == 0);
OsGdbSendPacket(g_serialBuf, ret);
return 0;
}
* Write the value of the CPU registers
* Format: G XX...
*/
static STUB_TEXT int GdbCmdWriteAllRegs(U8 *ptr, int len)
{
int ret = OsGdbArchWriteAllRegs(g_coreId, ptr, len);
CHECK_ERROR(ret == 0);
OsGdbSendPacket("OK", 2);
return 0;
}
static STUB_TEXT int GdbCmdThreadSet(U8 *ptr, int len)
{
if (*ptr == 's') {
OsGdbSendPacket(NULL, 0);
return 0;
}
if (GdbCmdSetThread(ptr, len) == 0) {
OsGdbSendPacket("OK", 2);
} else {
OsGdbSendPacket(GDB_ERROR_INVAL, 3);
}
return 0;
}
static STUB_TEXT int GdbCmdExit(U8 *ptr, int len)
{
(void)ptr;
(void)len;
GdbResetBkpts();
OsGdbArchRemoveAllHwBkpts();
OsGdbArchContinue(g_coreId);
state = EXIT;
g_exitDbg = 1;
return 0;
}
static STUB_TEXT int GdbCmdDfx(U8 *ptr, int len)
{
(void)ptr;
(void)len;
OsGdbSendPacket("OK", 2);
return 0;
}
static STUB_TEXT int GdbCmdNotSupport(U8 *ptr, int len)
{
(void)ptr;
(void)len;
OsGdbSendPacket(NULL, 0);
return 0;
}
static STUB_TEXT int GdbCmdNoRsp(U8 *ptr, int len)
{
(void)ptr;
(void)len;
return 0;
}
typedef int (*GdbCmdHandler) (U8 *buf, int len);
typedef struct GdbCmdRegEntryT {
char code;
GdbCmdHandler handler;
} GdbCmdRegEntry;
static STUB_DATA GdbCmdRegEntry g_cmdRegTbl[] = {
{'m',GdbCmdMemRead},
{'M',GdbCmdMemWrite},
{'c',GdbCmdContinue},
{'s',GdbCmdStep},
{'g',GdbCmdReadAllRegs},
{'G',GdbCmdWriteAllRegs},
{'p',GdbCmdReadReg},
{'P',GdbCmdWriteReg},
{'z',GdbCmdBreak},
{'Z',GdbCmdBreak},
{'?',GdbSendStopReply},
{'H',GdbCmdThreadSet},
{'T',GdbCmdThreadAlive},
{'q',GdbCmdQuery},
{'R',GdbCmdNoRsp},
{'k',GdbCmdExit},
{'j',GdbCmdDfx},
};
static STUB_DATA GdbCmdHandler g_gdbCmdHandlers[MAX_HANDLER_NUM];
static STUB_TEXT void GdbRegHandlers()
{
int len = sizeof(g_cmdRegTbl) / sizeof(GdbCmdRegEntry);
for (int i = 0; i < MAX_HANDLER_NUM; i++) {
g_gdbCmdHandlers[i] = GdbCmdNotSupport;
}
for (int i = 0; i < len; i++) {
g_gdbCmdHandlers[g_cmdRegTbl[i].code] = g_cmdRegTbl[i].handler;
}
}
* Synchronously communicate with gdb on the host
*/
static STUB_TEXT int GdbSerialStub()
{
state = RECEIVING;
if (g_notFirstStart) {
GdbSendStopReply(NULL, 0);
} else {
g_notFirstStart = 1;
}
g_prevCoreId = g_coreId = OsGdbGetCoreID();
while (state == RECEIVING) {
U8 *ptr;
int len;
int ret;
ret = OsGdbGetPacket(g_serialBuf, sizeof(g_serialBuf), &len);
if ((ret == -GDB_RSP_ENO_CHKSUM) || (ret == -GDB_RSP_ENO_2BIG)) {
OsGdbSendPacket(GDB_ERROR_GENERAL, 3);
continue;
}
if (len == 0) {
continue;
}
ptr = g_serialBuf;
char ch = *(ptr++);
ret = g_gdbCmdHandlers[ch](ptr, len - 1);
* If this is an recoverable error, send an error message to
* GDB and continue the debugging session.
*/
if (ret < 0) {
OsGdbSendPacket(GDB_ERROR_GENERAL, 3);
state = RECEIVING;
}
OsGdbFlush();
}
return 0;
}
#ifdef OS_OPTION_SMP
static STUB_TEXT void GdbRoundupCores(void)
{
U32 cid;
for (cid = g_firstCoreId; cid < OS_MAX_CORE_NUM; cid++) {
if (cid == OsGdbGetCoreID()) {
continue;
}
OsGdbArchForceStep(cid, true);
}
smp_mb();
}
static STUB_TEXT int GdbReturnNormal(U32 cid)
{
if (g_coreId == g_prevCoreId || cid != g_coreId) {
OsGdbArchContinue(cid);
}
os_asm_invalidate_icache_all();
g_excState[cid] &= ~(DCPU_WANT_MASTER | DCPU_IS_SLAVE);
smp_mb();
atomic_dec(&g_dbgSlaves);
return 0;
}
static STUB_TEXT int OsGdbCpuEnter(int excState)
{
int onlineCores = atomic_read(&g_onlineCores);
U32 cid = OsGdbGetCoreID();
g_excState[cid] |= excState;
if (g_exitDbg) {
raw_spin_lock(&g_dbgLock);
GdbDeactivateSwBkpts();
os_asm_invalidate_icache_all();
OsGdbArchContinue(cid);
raw_spin_unlock(&g_dbgLock);
return -1;
}
if (excState == DCPU_WANT_MASTER) {
atomic_inc(&g_dbgMasters);
} else {
atomic_inc(&g_dbgSlaves);
}
acquirelock:
smp_mb();
* CPU will loop if it is a slave or request to become a master cpu:
*/
while (1) {
if (g_excState[cid] & DCPU_WANT_MASTER) {
if (raw_spin_trylock(&g_dbgMasterLock)) {
break;
}
} else if (g_excState[cid] & DCPU_IS_SLAVE) {
if (!raw_spin_is_locked(&g_dbgSlaveLock)) {
return GdbReturnNormal(cid);
}
} else {
return GdbReturnNormal(cid);
}
cpu_relax();
}
* For single stepping, try to only enter on the processor that was single stepping.
*/
if (atomic_read(&g_ssCoreId) != -1 && (cid != atomic_read(&g_ssCoreId))) {
raw_spin_unlock(&g_dbgMasterLock);
nop_delay(1000);
goto acquirelock;
}
* Get the passive CPU lock which will hold all the non-primary
* CPU in a spin state while the debugger is active
*/
if (!g_ssFlg) {
raw_spin_lock(&g_dbgSlaveLock);
if ((atomic_read(&g_dbgMasters) + atomic_read(&g_dbgSlaves)) != onlineCores) {
GdbRoundupCores();
}
}
* Wait for the other CPUs to be notified and be waiting for us:
*/
while ((atomic_read(&g_dbgMasters) + atomic_read(&g_dbgSlaves)) != onlineCores) {
nop_delay(1000);
}
* At this point the primary processor is completely
* in the debugger and all secondary CPUs are quiescent
*/
GdbDeactivateSwBkpts();
g_ssFlg = 0;
GdbSerialStub();
GdbActivateSwBkpts();
if (!g_ssFlg) {
raw_spin_unlock(&g_dbgSlaveLock);
while (atomic_read(&g_dbgSlaves)) {
cpu_relax();
}
}
g_excState[cid] &= ~(DCPU_WANT_MASTER | DCPU_IS_SLAVE);
smp_mb();
atomic_dec(&g_dbgMasters);
raw_spin_unlock(&g_dbgMasterLock);
return 0;
}
STUB_TEXT int OsGdbStubSmpInit(void)
{
U32 cid = OsGdbGetCoreID();
OsGdbSmpArchInit();
raw_spin_lock(&g_initLock);
atomic_inc(&g_onlineCores);
g_onlineBitmap |= (1U << cid);
raw_spin_unlock(&g_initLock);
return 0;
}
STUB_TEXT void OsGdbHandleException(void *stk)
{
int excState = OsGdbArchPrepare(stk);
OsGdbArchDisableHwBkpts();
OsGdbCpuEnter(excState);
OsGdbArchCorrectHwBkpts();
OsGdbArchFinish(stk);
}
#else
STUB_TEXT void OsGdbHandleException(void *stk)
{
g_gdbActive = 1;
OsGdbArchPrepare(stk);
OsGdbArchDisableHwBkpts();
GdbDeactivateSwBkpts();
GdbSerialStub();
GdbActivateSwBkpts();
OsGdbArchCorrectHwBkpts();
OsGdbArchFinish(stk);
g_gdbActive = 0;
}
#endif
STUB_TEXT int OsGdbReenterChk(void *stk)
{
(void)stk;
return g_gdbActive;
}
static STUB_TEXT int GdbNotifyDie(struct NotifierBlock *nb,
int action, void *data)
{
if (g_exitDbg) {
return NOTIFY_DONE;
}
return OsGdbArchNotifyDie(action, data);
}
static STUB_DATA struct NotifierBlock g_gdbNotifier = {
.call = GdbNotifyDie,
.priority = 999,
};
STUB_TEXT int OsGdbStubEarlyInit(void)
{
GdbRegHandlers();
OsGdbConfigInitMemRegions();
if (OsGdbRingBufferInit()) {
return -1;
}
OsRegisterDieNotifier(&g_gdbNotifier);
#ifdef OS_OPTION_SMP
OsGdbStubSmpInit();
#endif
OsGdbArchInit();
}
#ifdef OS_OPTION_SMP
extern U32 g_cfgPrimaryCore;
STUB_TEXT int OsGdbStubInit(void)
{
U32 cid = OsGdbGetCoreID();
if (cid == g_cfgPrimaryCore) {
g_firstCoreId = g_cfgPrimaryCore;
return OsGdbStubEarlyInit();
}
return OsGdbStubSmpInit();
}
#else
STUB_TEXT int OsGdbStubInit(void)
{
g_firstCoreId = 0;
return OsGdbStubEarlyInit();
}
#endif