diff --git a/doc/om_guide/gdbstub.md b/doc/om_guide/gdbstub.md index 0cbd5ab63114811e295b9f94538d27892f0a6264..fb366d374a591776de3317a4b066e172d62da166 100644 --- a/doc/om_guide/gdbstub.md +++ b/doc/om_guide/gdbstub.md @@ -1,79 +1,89 @@ -# UniProton gdbstub使用指南 - -## 1 编译时使能gdbstub功能: -* defconfig 设置CONFIG_OS_GDB_STUB=y -* aarch64需要额外设置CONFIG_OS_OPTION_POWEROFF=y -* 如需调试内核代码,在config.xml中将UniProton_compile_mode设置为debug - -## 2 如何进行调试: -混合部署场景下,需要在linux上实现转发模块,用于在gdbstub和gdb之间进行消息转发。 -当前在mica中已经集成了转发的功能,在编译了支持gdbstub功能的elf后,可以直接使用mica进行调试。 -可以参考[使用方法和示例](https://embedded.pages.openeuler.org/master/features/mica/instruction.html)。 - -注: 在x86_64版本中需要额外将gdbmgr拷到验证环境上。 - -## 3 gdbstub支持情况 -当前支持架构为aarch64/x86_64,已经支持的demo如下: -* x86_64 (未适配资源表) -* hi3093 -* raspi4 -* kp920 (未适配资源表) - -当前主要支持命令及功能如下: -* break -* continue -* print (不包括调用函数) -* quit -* backtrace -* run -* watch -* Ctrl+C -* delete -* finish -* step -* next -* info local/regs - -## 4 适配指南 -aarch64/x86_64新增demo的适配主要包括以下两步: -* 链接脚本适配:参照现有demo补充缺失符号 -* ringbuffer适配:如果已经支持资源表,可跳过此步,无需进行适配 - -UniProton和linux侧的转发进程之间通过ringbuffer进行通讯,对于未适配资源表的场景,需要手动适配ringbuffer,具体可参考demos/x86_64/bsp/gdbstub_cfg.c。 -转发程序中ringbuffer的rxaddr和txaddr与UniProton相反,size相同。 -例如,UniProton侧配置的虚拟地址如下: -``` - .rxaddr = 0xf02600000 - 0x3000, - .txaddr = 0xf02600000 - 0x4000, - .size = 0x1000 -``` -linux侧配置的是物理地址,假设虚拟地址0xf02600000对应的物理地址是0x400000000, -那么linux侧的ringbuffer配置应为: -``` - .rxaddr = 0x400000000 - 0x4000, - .txaddr = 0x400000000 - 0x3000, - .size = 0x1000 -``` - -## 5 其他注意事项 -当前的gdbstub需要对代码段进行修改,对应的页表项上需要加上额外的写权限。修改方法如下: -### aarch64 -参考demos/raspi4/bsp/mmu.c,确定有MMU_ACCESS_RWX -```c - .virt = MMU_IMAGE_ADDR, - .phys = MMU_IMAGE_ADDR, - .size = 0x1000000, - .max_level = 0x2, - .attrs = MMU_ATTR_CACHE_SHARE | MMU_ACCESS_RWX, -``` - -### x86_64 -使用的页表有linux侧提前初始化好,因此需要修改mcs_km的[代码](https://gitee.com/openeuler/mcs/blob/uniproton_dev/mcs_km/mmu_map.c) -```c - // text - .va = 0xf02600000, - .pa = 0x0, - .size = 0x400000, - .attr = MEM_ATTR_CACHE_RWX, - .page_size = PAGE_SIZE_2M, +# UniProton gdbstub使用指南 + +## 1 编译时使能gdbstub功能: +* defconfig 设置CONFIG_OS_GDB_STUB=y +* aarch64需要额外设置CONFIG_OS_OPTION_POWEROFF=y +* 如需调试内核代码,在config.xml中将UniProton_compile_mode设置为debug + +## 2 如何进行调试: +混合部署场景下,需要在linux上实现转发模块,用于在gdbstub和gdb之间进行消息转发。 +当前在mica中已经集成了转发的功能,在编译了支持gdbstub功能的elf后,可以直接使用mica进行调试。 +可以参考[使用方法和示例](https://embedded.pages.openeuler.org/master/features/mica/instruction.html)。 + +注: 在x86_64版本中需要额外将gdbmgr拷到验证环境上。 + +## 3 gdbstub支持情况 +### 3.1 单核支持 +当前支持架构为aarch64/x86_64,已经支持的demo如下: +* x86_64 (未适配资源表) +* hi3093 +* raspi4 +* kp920 (未适配资源表) + +当前主要支持命令及功能如下: +* break +* continue +* print (不包括调用函数) +* quit +* backtrace +* run +* watch +* Ctrl+C +* delete +* finish +* step +* next +* info local/regs +### 3.2 多核支持 +仅支持aarch64,编译配置参考单核,无需额外设置。 +仅支持3.1中除run以外的基础调试命令功能 +不支持调度锁 scheduler-locking + +## 4 适配指南 +aarch64/x86_64新增demo的适配主要包括以下两步: +* 链接脚本适配:参照现有demo补充缺失符号 +* ringbuffer适配:如果已经支持资源表,可跳过此步,无需进行适配 + +UniProton和linux侧的转发进程之间通过ringbuffer进行通讯,对于未适配资源表的场景,需要手动适配ringbuffer,具体可参考demos/x86_64/bsp/gdbstub_cfg.c。 +转发程序中ringbuffer的rxaddr和txaddr与UniProton相反,size相同。 +例如,UniProton侧配置的虚拟地址如下: +``` + .rxaddr = 0xf02600000 - 0x3000, + .txaddr = 0xf02600000 - 0x4000, + .size = 0x1000 +``` +linux侧配置的是物理地址,假设虚拟地址0xf02600000对应的物理地址是0x400000000, +那么linux侧的ringbuffer配置应为: +``` + .rxaddr = 0x400000000 - 0x4000, + .txaddr = 0x400000000 - 0x3000, + .size = 0x1000 +``` + +## 5 其他注意事项 +### 线程映射关系 +UniProton侧管理的一个核映射为GDB侧一个线程,因此单步操作后任务可能会发生切换 +### 权限配置 +当前的gdbstub需要对代码段进行修改,对应的页表项上需要加上额外的写权限。修改方法如下: + +#### aarch64 + +参考demos/raspi4/bsp/mmu.c,确定有MMU_ACCESS_RWX +```c + .virt = MMU_IMAGE_ADDR, + .phys = MMU_IMAGE_ADDR, + .size = 0x1000000, + .max_level = 0x2, + .attrs = MMU_ATTR_CACHE_SHARE | MMU_ACCESS_RWX, +``` + +#### x86_64 +使用的页表有linux侧提前初始化好,因此需要修改mcs_km的[代码](https://gitee.com/openeuler/mcs/blob/uniproton_dev/mcs_km/mmu_map.c) +```c + // text + .va = 0xf02600000, + .pa = 0x0, + .size = 0x400000, + .attr = MEM_ATTR_CACHE_RWX, + .page_size = PAGE_SIZE_2M, ``` \ No newline at end of file diff --git a/src/arch/cpu/armv8/common/hwi/prt_reset_vector.S b/src/arch/cpu/armv8/common/hwi/prt_reset_vector.S index 49dfa6ac79ceed71a1e3bc84782a1847969477c1..bc06062f24479f17744a4bbcdd45606e2ecb4e20 100644 --- a/src/arch/cpu/armv8/common/hwi/prt_reset_vector.S +++ b/src/arch/cpu/armv8/common/hwi/prt_reset_vector.S @@ -144,11 +144,11 @@ OsPrimaryMain: /* OsPrimaryMain函数主核调用 */ #if defined(OS_OPTION_GUARD_STACK) InitChkGuardRnd x9, x10, w11 /* RND 写入 StackChkGuard */ #endif -#ifdef OS_GDB_STUB - bl OsGdbStubInit -#endif OsEnterMain: +#ifdef OS_GDB_STUB + BL OsGdbStubInit +#endif BL main MOV x2, DAIF_MASK diff --git a/src/arch/cpu/armv8/common/hwi/prt_vector.S b/src/arch/cpu/armv8/common/hwi/prt_vector.S index afdfb7e41281cbc79d3c72190cb465f03b8d7392..be63ec3bf948ee9954333412eaaa26074770a573 100644 --- a/src/arch/cpu/armv8/common/hwi/prt_vector.S +++ b/src/arch/cpu/armv8/common/hwi/prt_vector.S @@ -59,8 +59,6 @@ el1_dbg: mov x0, sp bl OsGdbHandleException - msr daifset, 0xf - ldp x22, x23, [sp, #S_PC] // load ELR, SPSR msr elr_el1, x22 // set up the return data msr spsr_el1, x23 diff --git a/src/component/gdbstub/arch/arm64/gdbstub.c b/src/component/gdbstub/arch/arm64/gdbstub.c index 17a25166ad7d9a6ece90fff9c6bb8b765969bab3..8f67d8a3e53c6a991cfaa0bccb3dd2f86c9aad10 100644 --- a/src/component/gdbstub/arch/arm64/gdbstub.c +++ b/src/component/gdbstub/arch/arm64/gdbstub.c @@ -15,11 +15,14 @@ */ #include "prt_typedef.h" +#include "prt_buildef.h" #include "prt_notifier.h" #include "gdbstub.h" #include "arch_interface.h" #include "rsp_utils.h" - +#ifdef OS_OPTION_SMP +#include "hw/armv8/os_cpu_armv8.h" +#endif STUB_DATA static struct DbgRegDef g_dbgRegDef[DBG_MAX_REG_NUM] = { { "x0", 8, offsetof(struct PrtRegs, regs[0]) }, { "x1", 8, offsetof(struct PrtRegs, regs[1]) }, @@ -97,13 +100,14 @@ STUB_DATA static struct DbgRegDef g_dbgRegDef[DBG_MAX_REG_NUM] = { { "fpcr", 4, -1 }, }; -STUB_DATA static struct GdbCtx g_debugContext; +STUB_DATA static struct GdbCtx g_dbgCtx[MAX_CORE_NUM]; +#define REGS(cid) (g_dbgCtx[cid].regs.regs) +#define RBUF(cid) (g_dbgCtx[cid].regs.rbuf) -STUB_DATA static bool g_compiled_brk = false; +#define CTX(cid) (g_dbgCtx[cid]) -STUB_DATA static bool g_stopByCtrlc; - -STUB_DATA static bool g_needStepFlg; +#define LAST_CTRL_CONT 1 +#define LAST_CTRL_STEP 2 STUB_DATA static uint8_t g_alignedLenArray[] = {1,2,4,4,8,8,8,8}; @@ -121,9 +125,6 @@ STUB_DATA static uint8_t g_basArray[] = { ARM_BREAKPOINT_LEN_7, ARM_BREAKPOINT_LEN_8, }; -#define REGS (g_debugContext.regs.regs) -#define RBUF (g_debugContext.regs.rbuf) - /* mask/save/unmask/restore all exceptions, including interrupts. */ static inline void local_daif_mask(void) { @@ -134,15 +135,6 @@ static inline void local_daif_mask(void) : "memory"); } -static inline void local_daif_clr_flags(uint32_t flags) -{ - __asm__ volatile( - "msr daifset, %0" - : - : "i"(flags) - : "memory"); -} - static inline unsigned long local_daif_save_flags(void) { unsigned long flags; @@ -182,74 +174,100 @@ STUB_TEXT static uint32_t mdscr_read(void) return read_sysreg(mdscr_el1); } -STUB_TEXT void OsGdbArchContinue(void) +STUB_TEXT void OsGdbArchForceStep(U32 cid, bool slaveFlg) { - if (g_compiled_brk) { - REGS.pc += BREAK_INSTR_SIZE; - g_compiled_brk = false; - } - - REGS.pstate &= ~DBG_SPSR_SS; - REGS.pstate &= ~DBG_SPSR_D; - - mdscr_write(mdscr_read() & ~DBG_MDSCR_SS); + CTX(cid).needStepFlg = true; + CTX(cid).slaveFlg = slaveFlg; } -STUB_TEXT void OsGdbArchStep(void) +STUB_TEXT int OsGdbArchContinue(U32 cid) { - if (g_compiled_brk) { - REGS.pc += BREAK_INSTR_SIZE; - g_compiled_brk = false; + if (cid >= MAX_CORE_NUM) { + return -1; } + if (CTX(cid).compiledBrk) { + REGS(cid).pc += BREAK_INSTR_SIZE; + CTX(cid).compiledBrk = false; + } + CTX(cid).slaveFlg = false; + REGS(cid).pstate &= ~DBG_SPSR_SS; + REGS(cid).pstate &= ~DBG_SPSR_D; + CTX(cid).lastCtrl = LAST_CTRL_CONT; + return 0; +} - REGS.pstate |= DBG_SPSR_SS; - REGS.pstate &= ~DBG_SPSR_D; - mdscr_write(mdscr_read() | DBG_MDSCR_SS | DBG_MDSCR_KDE); +STUB_TEXT int OsGdbArchStep(U32 cid) +{ + if (cid >= MAX_CORE_NUM) { + return -1; + } + if (CTX(cid).compiledBrk) { + REGS(cid).pc += BREAK_INSTR_SIZE; + CTX(cid).compiledBrk = false; + } + CTX(cid).slaveFlg = false; + REGS(cid).pstate |= DBG_SPSR_SS; + REGS(cid).pstate &= ~DBG_SPSR_D; + CTX(cid).lastCtrl = LAST_CTRL_STEP; + return 0; } STUB_TEXT void OsGdbMarkStep(uint64_t *sp) { - if (LIKELY(!g_needStepFlg)) { + U32 cid = OsGdbGetCoreID(); + if (LIKELY(!CTX(cid).needStepFlg)) { return; } - g_needStepFlg = false; + CTX(cid).needStepFlg = false; + sp[1] |= DBG_SPSR_SS; sp[1] &= ~DBG_SPSR_D; mdscr_write(mdscr_read() | DBG_MDSCR_SS | DBG_MDSCR_KDE); } -STUB_TEXT void OsGdbArchPrepare(void *stk) +STUB_TEXT int OsGdbArchPrepare(void *stk) { struct PrtRegs *orig = (struct PrtRegs *)stk; - + U32 cid = OsGdbGetCoreID(); + int state = DCPU_WANT_MASTER; for (int i = 0; i < sizeof(orig->regs) / sizeof(orig->regs[0]); i++) { - REGS.regs[i] = orig->regs[i]; + REGS(cid).regs[i] = orig->regs[i]; } - - REGS.sp = orig->sp; - REGS.pc = orig->pc; - REGS.pstate = orig->pstate; - g_debugContext.far = 0; + REGS(cid).sp = orig->sp; + REGS(cid).pc = orig->pc; + REGS(cid).pstate = orig->pstate; + CTX(cid).far = 0; uint32_t esr = read_sysreg(esr_el1); - g_debugContext.ec = ESR_ELx_EC(esr); + CTX(cid).ec = ESR_ELx_EC(esr); if ((esr & ESR_ELx_BRK64_ISS_COMMENT_MASK) == GDB_COMPILED_DBG_BRK_IMM) { - g_compiled_brk = true; + CTX(cid).compiledBrk = true; + } else if (CTX(cid).slaveFlg) { + CTX(cid).slaveFlg = false; + state = DCPU_IS_SLAVE; } else if (ESR_ELx_EC(esr) == ESR_ELx_EC_WATCHPT_CUR) { - g_debugContext.far = read_sysreg(far_el1); + CTX(cid).far = read_sysreg(far_el1); } + return state; } STUB_TEXT void OsGdbArchFinish(void *stk) { struct PrtRegs *orig = (struct PrtRegs *)stk; - + U32 cid = OsGdbGetCoreID(); + int ctrl = CTX(cid).lastCtrl; for (int i = 0; i < sizeof(orig->regs) / sizeof(orig->regs[0]); i++) { - orig->regs[i] = REGS.regs[i]; + orig->regs[i] = REGS(cid).regs[i]; + } + orig->sp = REGS(cid).sp; + orig->pc = REGS(cid).pc; + orig->pstate = REGS(cid).pstate; + + if (ctrl == LAST_CTRL_CONT) { + mdscr_write(mdscr_read() & ~DBG_MDSCR_SS); + } else if (ctrl == LAST_CTRL_STEP) { + mdscr_write(mdscr_read() | DBG_MDSCR_SS | DBG_MDSCR_KDE); } - orig->sp = REGS.sp; - orig->pc = REGS.pc; - orig->pstate = REGS.pstate; } static inline int GdbReadRegUnavailable(U32 regno, U8 *buf) @@ -263,7 +281,7 @@ static inline int GdbReadRegUnavailable(U32 regno, U8 *buf) return size; } -STUB_TEXT static int GdbReadOneReg(U32 regno, U8 *buf) +STUB_TEXT static int GdbReadOneReg(U32 cid, U32 regno, U8 *buf) { int len; @@ -271,13 +289,13 @@ STUB_TEXT static int GdbReadOneReg(U32 regno, U8 *buf) return GdbReadRegUnavailable(regno, buf); } - len = OsGdbBin2Hex(&RBUF[g_dbgRegDef[regno].offset], g_dbgRegDef[regno].size, + len = OsGdbBin2Hex(&(RBUF(cid)[g_dbgRegDef[regno].offset]), g_dbgRegDef[regno].size, buf, g_dbgRegDef[regno].size * BYTE_TO_STR_LEN + 1); - return len; + return len; } -STUB_TEXT static int GdbWriteOneReg(U32 regno, U8 *buf) +STUB_TEXT static int GdbWriteOneReg(U32 cid, U32 regno, U8 *buf) { int len; @@ -286,11 +304,11 @@ STUB_TEXT static int GdbWriteOneReg(U32 regno, U8 *buf) } len = OsGdbHex2Bin(buf, g_dbgRegDef[regno].size * BYTE_TO_STR_LEN, - &RBUF[g_dbgRegDef[regno].offset], g_dbgRegDef[regno].size); + &(RBUF(cid)[g_dbgRegDef[regno].offset]), g_dbgRegDef[regno].size); return BYTE_TO_STR_LEN * len; } -STUB_TEXT int OsGdbArchReadAllRegs(U8 *buf, int buflen) +STUB_TEXT int OsGdbArchReadAllRegs(U32 cid, U8 *buf, int buflen) { int ret = 0; @@ -299,43 +317,44 @@ STUB_TEXT int OsGdbArchReadAllRegs(U8 *buf, int buflen) } for (int i = 0; i < DBG_MAX_REG_NUM; i++) { - ret += GdbReadOneReg(i, &buf[ret]); + ret += GdbReadOneReg(cid, i, &buf[ret]); } return ret; } -STUB_TEXT int OsGdbArchWriteAllRegs(U8 *hex, int hexlen) +STUB_TEXT int OsGdbArchWriteAllRegs(U32 cid, U8 *hex, int hexlen) { int ret = 0; - if (hexlen < BYTE_TO_STR_LEN * NUMREGBYTES) { + if (cid >= MAX_CORE_NUM || hexlen < BYTE_TO_STR_LEN * NUMREGBYTES) { return 0; } for (int i = 0; i < DBG_MAX_REG_NUM; i++) { - ret += GdbWriteOneReg(i, &hex[ret]); + ret += GdbWriteOneReg(cid, i, &hex[ret]); } return ret; } -STUB_TEXT int OsGdbArchReadReg(U32 regno, U8 *buf, int buflen) +STUB_TEXT int OsGdbArchReadReg(U32 cid, U32 regno, U8 *buf, int buflen) { - if (regno >= DBG_MAX_REG_NUM || buflen < 2 * g_dbgRegDef[regno].size + 1) { + if (cid >= MAX_CORE_NUM || regno >= DBG_MAX_REG_NUM || + buflen < 2 * g_dbgRegDef[regno].size + 1) { return 0; } - return GdbReadOneReg(regno, buf); + return GdbReadOneReg(cid, regno, buf); } -STUB_TEXT int OsGdbArchWriteReg(U32 regno, U8 *buf, int buflen) +STUB_TEXT int OsGdbArchWriteReg(U32 cid, U32 regno, U8 *buf, int buflen) { if (regno >= DBG_MAX_REG_NUM || buflen < 2 * g_dbgRegDef[regno].size) { return -1; } - return GdbWriteOneReg(regno, buf); + return GdbWriteOneReg(cid, regno, buf); } STUB_TEXT int OsGdbArchSetSwBkpt(struct GdbBkpt *bkpt) @@ -361,10 +380,9 @@ static inline void InsertCompiledBrk() "dsb st\n" "brk %0\n" "dsb st\n" - : : "I" (GDB_COMPILED_DBG_BRK_IMM)); + : : "i" (GDB_COMPILED_DBG_BRK_IMM)); } - STUB_TEXT static void OsCacheFlush(unsigned long addr_start, unsigned long addr_end) { uint64_t cache_line_size; @@ -440,14 +458,8 @@ static STUB_TEXT void WriteWbReg(int reg, int n, uint64_t val) idx; \ }); -STUB_TEXT void OsGdbArchInit(void) +STUB_TEXT void OsGdbSmpArchInit(void) { - /* Determine number of BRP/WRP registers available. */ - uint64_t aa64dfr0 = read_sysreg(id_aa64dfr0_el1); - - g_maxBrpCnt = AA64DFR0_BRPS_VAL(aa64dfr0); - g_maxWrpCnt = AA64DFR0_WRPS_VAL(aa64dfr0); - for (int i = 0; i < g_maxBrpCnt; i++) { WriteWbReg(AARCH64_DBG_REG_BVR, i, 0); WriteWbReg(AARCH64_DBG_REG_BCR, i, 0); @@ -458,7 +470,17 @@ STUB_TEXT void OsGdbArchInit(void) } write_sysreg(0, osdlr_el1); write_sysreg(0, oslar_el1); +} + +STUB_TEXT void OsGdbArchInit(void) +{ + /* Determine number of BRP/WRP registers available. */ + uint64_t aa64dfr0 = read_sysreg(id_aa64dfr0_el1); + g_maxBrpCnt = AA64DFR0_BRPS_VAL(aa64dfr0); + g_maxWrpCnt = AA64DFR0_WRPS_VAL(aa64dfr0); + + OsGdbSmpArchInit(); InsertCompiledBrk(); } @@ -640,13 +662,15 @@ STUB_TEXT int OsGdbArchHitHwBkpt(uintptr_t *addr, unsigned *type) { uint64_t far; struct HwBreakpointCtrl ctrl = {0}; - if (addr == NULL || type == NULL || g_debugContext.far == 0) { + U32 cid = OsGdbGetCoreID(); + + if (addr == NULL || type == NULL || cid >= MAX_CORE_NUM || CTX(cid).far == 0) { return 0; } - if (g_debugContext.ec != ESR_ELx_EC_WATCHPT_CUR) { + if (CTX(cid).ec != ESR_ELx_EC_WATCHPT_CUR) { return 0; } - far = g_debugContext.far; + far = CTX(cid).far; for (int i = 0; i < g_maxWrpCnt; i++) { if (g_wrpArray[i].state == 0) { continue; @@ -680,9 +704,10 @@ STUB_TEXT void OsGdbArchDisableHwBkpts() STUB_TEXT int OsGdbArchNotifyDie(int action, void *data) { - g_stopByCtrlc = true; + U32 cid = OsGdbGetCoreID(); + CTX(cid).stopByCtrlc = true; #ifdef LAZY_STOP - g_needStepFlg = true; + CTX(cid).needStepFlg = true; #else InsertCompiledBrk(); #endif @@ -691,9 +716,21 @@ STUB_TEXT int OsGdbArchNotifyDie(int action, void *data) STUB_TEXT int OsGdbGetStopReason() { - if (g_stopByCtrlc) { - g_stopByCtrlc = false; + U32 cid = OsGdbGetCoreID(); + if (CTX(cid).stopByCtrlc) { + CTX(cid).stopByCtrlc = false; return 2; } return 5; -} \ No newline at end of file +} + +#ifdef OS_OPTION_SMP +STUB_TEXT U32 OsGdbGetCoreID(void) +{ + return OsGetCoreID(); +} +#else +STUB_TEXT U32 OsGdbGetCoreID(void) { + return 0; +} +#endif \ No newline at end of file diff --git a/src/component/gdbstub/arch/arm64/gdbstub.h b/src/component/gdbstub/arch/arm64/gdbstub.h index 6a932aebb3ef57dec32e2b3e76aa6b7032779589..6325c7f170eb07aa2a0a0a37a3254a5bd3a5d8d1 100644 --- a/src/component/gdbstub/arch/arm64/gdbstub.h +++ b/src/component/gdbstub/arch/arm64/gdbstub.h @@ -18,6 +18,7 @@ #define _GDBSTUB_H_ #include "prt_typedef.h" +#include "prt_buildef.h" #define _GP_REGS 33 #define _FP_REGS 32 @@ -242,6 +243,11 @@ struct GdbCtx } regs; uint64_t far; uint8_t ec; + bool compiledBrk; + bool stopByCtrlc; + bool needStepFlg; + bool slaveFlg; + int lastCtrl; }; typedef struct HwBreakpoint { @@ -268,4 +274,29 @@ struct HwBreakpointCtrl { enabled : 1; }; +#ifdef OS_OPTION_SMP +#define MAX_CORE_NUM OS_MAX_CORE_NUM +#else +#define MAX_CORE_NUM 1 +#endif + +#define dmb(opt) __asm__ volatile("dmb " #opt : : : "memory") +#define smp_mb() dmb(ish) +#define smp_rmb() dmb(ishld) +#define smp_wmb() dmb(ishst) + +static inline void cpu_relax() +{ + __asm__ volatile("yield" ::: "memory"); +} + +static inline void nop_delay(U32 loop) { + while (loop--) { + __asm__ volatile("nop"); + } +} + +extern void os_asm_invalidate_icache_all(void); + +extern U32 OsGdbGetCoreID(void); #endif /* _GDBSTUB_H_ */ diff --git a/src/component/gdbstub/arch/x86_64/gdbstub.c b/src/component/gdbstub/arch/x86_64/gdbstub.c index b0d38581c33e388a1d30abc24cc7f2af4f98e3cf..f0c4465fbfcae21131f06f74401204cfa0c26018 100644 --- a/src/component/gdbstub/arch/x86_64/gdbstub.c +++ b/src/component/gdbstub/arch/x86_64/gdbstub.c @@ -56,19 +56,23 @@ static STUB_DATA struct DbgRegDef g_dbgRegDef[DBG_MAX_REG_NUM] = #define REGS (g_debugContext.regs.regs) #define RBUF (g_debugContext.regs.rbuf) -STUB_TEXT void OsGdbArchContinue(void) +STUB_TEXT int OsGdbArchContinue(U32 cid) { + (void)(cid); /* Clear the TRAP FLAG bit */ REGS.flags &= ~BIT(8); + return 0; } -STUB_TEXT void OsGdbArchStep(void) +STUB_TEXT int OsGdbArchStep(U32 cid) { + (void)(cid); /* Set the TRAP FLAG bit */ REGS.flags |= BIT(8); + return 0; } -STUB_TEXT void OsGdbArchPrepare(void *stk) +STUB_TEXT int OsGdbArchPrepare(void *stk) { struct PrtRegs *orig = (struct PrtRegs *)stk; REGS.ax = orig->ax; @@ -95,6 +99,7 @@ STUB_TEXT void OsGdbArchPrepare(void *stk) REGS.flags = orig->flags; REGS.sp = orig->sp; REGS.ss = orig->ss; + return DCPU_WANT_MASTER; } STUB_TEXT void OsGdbArchFinish(void *stk) @@ -160,8 +165,9 @@ INLINE int GdbWriteOneReg(U32 regno, U8 *buf) g_dbgRegDef[regno].size); } -STUB_TEXT int OsGdbArchReadReg(U32 regno, U8 *buf, int buflen) +STUB_TEXT int OsGdbArchReadReg(U32 cid, U32 regno, U8 *buf, int buflen) { + (void)(cid); if ((regno >= DBG_MAX_REG_NUM) || (buflen < 2 * g_dbgRegDef[regno].size + 1)) { return 0; @@ -169,8 +175,9 @@ STUB_TEXT int OsGdbArchReadReg(U32 regno, U8 *buf, int buflen) return GdbReadOneReg(regno, buf); } -STUB_TEXT int OsGdbArchReadAllRegs(U8 *buf, int buflen) +STUB_TEXT int OsGdbArchReadAllRegs(U32 cid, U8 *buf, int buflen) { + (void)(cid); int ret = 0; if (buflen < 2 * NUMREGBYTES) { @@ -182,8 +189,9 @@ STUB_TEXT int OsGdbArchReadAllRegs(U8 *buf, int buflen) return ret; } -STUB_TEXT int OsGdbArchWriteReg(U32 regno, U8 *buf, int buflen) +STUB_TEXT int OsGdbArchWriteReg(U32 cid, U32 regno, U8 *buf, int buflen) { + (void)(cid); if (regno == GDB_SP || regno == GDB_ORIG_AX) { return 0; } @@ -194,8 +202,9 @@ STUB_TEXT int OsGdbArchWriteReg(U32 regno, U8 *buf, int buflen) return GdbWriteOneReg(regno, buf); } -STUB_TEXT int OsGdbArchWriteAllRegs(U8 *hex, int hexlen) +STUB_TEXT int OsGdbArchWriteAllRegs(U32 cid, U8 *hex, int hexlen) { + (void)(cid); int ret = 0; if (hexlen < 2 * NUMREGBYTES) { @@ -485,4 +494,8 @@ STUB_TEXT int OsGdbArchNotifyDie(int action, void *data) STUB_TEXT int OsGdbGetStopReason() { return g_stopReason; +} + +STUB_TEXT U32 OsGdbGetCoreID(void) { + return 0; } \ No newline at end of file diff --git a/src/component/gdbstub/arch/x86_64/gdbstub.h b/src/component/gdbstub/arch/x86_64/gdbstub.h index 4895c7d9374c14ed6937fd12f89823bf66cc2842..0585da5a99ec8cf362d1028b8a9fd655cf2da4bc 100644 --- a/src/component/gdbstub/arch/x86_64/gdbstub.h +++ b/src/component/gdbstub/arch/x86_64/gdbstub.h @@ -2,6 +2,7 @@ #define _GDBSTUB_H_ #include "prt_typedef.h" +#include "prt_buildef.h" #define BREAK_INSTR_SIZE 1 /* 17 64 bit regs and 5 32 bit regs */ @@ -121,6 +122,8 @@ /* Total number of available HW breakpoint registers */ #define HBP_NUM 4 +#define MAX_CORE_NUM 1 + enum RegNames { GDB_AX, /* 0 */ GDB_BX, /* 1 */ @@ -191,4 +194,6 @@ struct HwBrkInfo { uint8_t enabled; uint8_t type; }; + +extern U32 OsGdbGetCoreID(void); #endif /* _GDBSTUB_H_ */ diff --git a/src/component/gdbstub/gdbstub_common.c b/src/component/gdbstub/gdbstub_common.c index 62b3a369c75a1c87634c667bab48fcfa038862d8..7bf45aa7ed53ce50ca35baf424cf2785b3797570 100644 --- a/src/component/gdbstub/gdbstub_common.c +++ b/src/component/gdbstub/gdbstub_common.c @@ -22,6 +22,9 @@ #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 @@ -54,9 +57,79 @@ enum LoopState { EXIT, } state; +#define MAX_HANDLER_NUM 128 + static STUB_DATA char g_notFirstStart; static STUB_DATA char g_gdbActive; -static STUB_DATA char g_exitDbg; +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}; + +/* to keep track of the CPU which is doing the single stepping*/ +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. @@ -150,11 +223,11 @@ STUB_TEXT int OsGdbConfigInitMemRegions(void) g_regions[2].attributes = GDB_MEM_REGION_NO_BKPT; } -static STUB_TEXT int GdbAddrCheck(uintptr_t addr, int attr) +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 < g_regions[i].end && + addr + len < g_regions[i].end && (g_regions[i].attributes & attr) == attr) { return 0; } @@ -162,19 +235,19 @@ static STUB_TEXT int GdbAddrCheck(uintptr_t addr, int attr) return -EINVAL; } -static STUB_TEXT int GdbInvalidReadAddr(uintptr_t addr) +static STUB_TEXT int GdbInvalidReadAddr(uintptr_t addr, int len) { - return GdbAddrCheck(addr, GDB_MEM_REGION_READ); + return GdbAddrCheck(addr, len, GDB_MEM_REGION_READ); } -static STUB_TEXT int GdbInvalidWriteAddr(uintptr_t addr) +static STUB_TEXT int GdbInvalidWriteAddr(uintptr_t addr, int len) { - return GdbAddrCheck(addr, GDB_MEM_REGION_WRITE); + return GdbAddrCheck(addr, len, GDB_MEM_REGION_WRITE); } static STUB_TEXT int GdbInvalidBkptAddr(uintptr_t addr) { - return !GdbAddrCheck(addr, GDB_MEM_REGION_NO_BKPT); + return !GdbAddrCheck(addr, BREAK_INSTR_SIZE, GDB_MEM_REGION_NO_BKPT); } /* @@ -361,8 +434,13 @@ static STUB_TEXT int GdbRawMemWrite(const U8 *buf, uintptr_t addr, int len) return count; } -static STUB_TEXT int GdbCmdMemRead(U8 *ptr) +/* + * 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; @@ -380,7 +458,7 @@ static STUB_TEXT int GdbCmdMemRead(U8 *ptr) * a fault. Just send a packet informing that * this address is invalid */ - if (GdbInvalidReadAddr(addr)) { + if (GdbInvalidReadAddr(addr, len)) { OsGdbSendPacket(GDB_ERROR_MEMORY, 3); return 0; } @@ -390,8 +468,13 @@ static STUB_TEXT int GdbCmdMemRead(U8 *ptr) return ret; } -static STUB_TEXT int GdbCmdMemWrite(U8 *ptr) +/* + * Write to memory + * Format: M addr,length:val + */ +static STUB_TEXT int GdbCmdMemWrite(U8 *ptr, int bufLen) { + (void)bufLen; int len; uintptr_t addr; @@ -400,7 +483,7 @@ static STUB_TEXT int GdbCmdMemWrite(U8 *ptr) CHECK_HEX(len); CHECK_CHAR(':'); - if (GdbInvalidWriteAddr(addr)) { + if (GdbInvalidWriteAddr(addr, len)) { OsGdbSendPacket(GDB_ERROR_MEMORY, 3); return 0; } @@ -412,8 +495,12 @@ static STUB_TEXT int GdbCmdMemWrite(U8 *ptr) return 0; } -static STUB_TEXT int GdbCmdBreak(U8 *ptr) +/* + * Breakpoints + */ +static STUB_TEXT int GdbCmdBreak(U8 *ptr, int len) { + (void)len; U32 kind; uintptr_t addr; U8 type; @@ -442,18 +529,27 @@ static STUB_TEXT int GdbCmdBreak(U8 *ptr) return 0; } -static STUB_TEXT int GdbCmdReadReg(U8 *ptr) +/* + * 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(regno, g_serialBuf, sizeof(g_serialBuf)); + 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; @@ -462,35 +558,287 @@ static STUB_TEXT int GdbCmdWriteReg(U8 *ptr, int len) CHECK_HEXS(regno, cnt); CHECK_CHAR('='); - ret = OsGdbArchWriteReg(regno, ptr, len - cnt - 1); + ret = OsGdbArchWriteReg(g_coreId, regno, ptr, len - cnt - 1); CHECK_ERROR(ret < 0) OsGdbSendPacket("OK", 2); return 0; } -static STUB_TEXT void GdbSendStopReply() +static STUB_TEXT int GdbSendStopReply(U8 *ptr, int len) { + (void)ptr; + (void)len; + // GDB_EXCEPTION_BREAKPOINT: DEBUG & BREAKPOINT: uintptr_t addr; unsigned type; - + U32 cid = OsGdbGetCoreID(); + int ret = 0; + int stopReason = OsGdbGetStopReason(); if (OsGdbArchHitHwBkpt(&addr, &type)) { const char *typeStr = GetWatchTypeStr(type); - int len = 0; - if (typeStr == NULL) { - OsGdbSendException(g_serialBuf, sizeof(g_serialBuf), OsGdbGetStopReason()); - return; + 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; +} - len = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "T05%s:%x;", typeStr, addr); - if (len < 0) { - OsGdbSendException(g_serialBuf, sizeof(g_serialBuf), OsGdbGetStopReason()); - return; +/* Handle the 'q' query packets */ +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; } - OsGdbSendPacket(g_serialBuf, len); + 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': + /* return Current thread id */ + ret = sprintf_s(g_serialBuf, sizeof(g_serialBuf) - 4, "QC%x;", OsGdbGetCoreID()); + OsGdbSendPacket(g_serialBuf, ret); + break; + + default: + OsGdbSendPacket(NULL, 0); + break; + } + return 0; +} + +/* Handle the 'H' task query packets */ +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; +} + +/* Handle the 'T' thread query packets */ +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 + /* We reset PC passively when receiving P/G packet. */ + 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 { - OsGdbSendException(g_serialBuf, sizeof(g_serialBuf), OsGdbGetStopReason()); + 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; } } @@ -500,26 +848,23 @@ static STUB_TEXT void GdbSendStopReply() static STUB_TEXT int GdbSerialStub() { state = RECEIVING; - /* Only send exception if this is not the first - * GDB break. - */ + + /* Only send exception if this is not the first GDB break. */ if (g_notFirstStart) { - GdbSendStopReply(); + 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); + + /* Send error and wait for next packet.*/ if ((ret == -GDB_RSP_ENO_CHKSUM) || (ret == -GDB_RSP_ENO_2BIG)) { - /* - * Send error and wait for next packet. - * - */ OsGdbSendPacket(GDB_ERROR_GENERAL, 3); continue; } @@ -529,136 +874,166 @@ static STUB_TEXT int GdbSerialStub() } ptr = g_serialBuf; - ret = 0; - switch (*ptr++) { - - /** - * Read from the memory - * Format: m addr,length - */ - case 'm': - ret = GdbCmdMemRead(ptr); - break; - - /** - * Write to memory - * Format: M addr,length:val - */ - case 'M': - ret = GdbCmdMemWrite(ptr); - break; + char ch = *(ptr++); + ret = g_gdbCmdHandlers[ch](ptr, len - 1); /* - * Continue ignoring the optional address - * Format: c addr + * If this is an recoverable error, send an error message to + * GDB and continue the debugging session. */ - case 'c': - OsGdbArchContinue(); - /* We reset PC passively when receiving P/G packet. */ - OsGdbSendPacket("OK", 2); - state = EXIT; - break; + if (ret < 0) { + OsGdbSendPacket(GDB_ERROR_GENERAL, 3); + state = RECEIVING; + } + OsGdbFlush(); + } /* while */ + return 0; +} - /* - * Step one instruction ignoring the optional address - * s addr..addr - */ - case 's': - OsGdbArchStep(); - state = EXIT; - break; +#ifdef OS_OPTION_SMP - /* - * Read all registers - * Format: g - */ - case 'g': - len = OsGdbArchReadAllRegs(g_serialBuf, sizeof(g_serialBuf)); - CHECK_ERROR(len == 0); - OsGdbSendPacket(g_serialBuf, len); - break; +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(); +} - /** - * Write the value of the CPU registers - * Format: G XX... - */ - case 'G': - len = OsGdbArchWriteAllRegs(ptr, len - 1); - CHECK_ERROR(len == 0); - OsGdbSendPacket("OK", 2); - break; +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; +} - /* - * Read register - * Format: p N - */ - case 'p': - ret = GdbCmdReadReg(ptr); - break; +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: - /** - * Write the value of the CPU register - * Format: P N...=XXX... - */ - case 'P': - ret = GdbCmdWriteReg(ptr, len - 1); - break; + /* Make sure the above info reaches the primary CPU */ + smp_mb(); - /* - * Breakpoints - */ - case 'z': /* __fallthrough */ - case 'Z': - ret = GdbCmdBreak(ptr); - break; + /* + * 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(); + } - /* What cause the pause */ - case '?': - OsGdbSendException(g_serialBuf, sizeof(g_serialBuf), - GDB_EXCEPTION_BREAKPOINT); - break; + /* + * 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; + } - case 'H': - if (*ptr == 'g' || *ptr == 'c' || *ptr == 's') { - OsGdbSendPacket("OK", 2); - } else { - OsGdbSendPacket(NULL, 0); - } - break; + /* + * 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); - case 'R': - break; + /* Signal the other CPUs to enter kgdb_wait() */ + if ((atomic_read(&g_dbgMasters) + atomic_read(&g_dbgSlaves)) != onlineCores) { + GdbRoundupCores(); + } + } - /* Exit debug */ - case 'k': - GdbResetBkpts(); - OsGdbArchRemoveAllHwBkpts(); - OsGdbArchContinue(); - state = EXIT; - g_exitDbg = 1; - break; + /* + * 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); + } - /* - * Not supported action - */ - default: - OsGdbSendPacket(NULL, 0); - break; - } /* switch */ + /* + * 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); - /* - * 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; + /* Wait till all the CPUs have quit from the debugger. */ + while (atomic_read(&g_dbgSlaves)) { + cpu_relax(); } - OsGdbFlush(); - } /* while */ + } + + 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; @@ -671,11 +1046,11 @@ STUB_TEXT void OsGdbHandleException(void *stk) OsGdbArchFinish(stk); g_gdbActive = 0; } +#endif STUB_TEXT int OsGdbReenterChk(void *stk) { (void)stk; - return g_gdbActive; } @@ -693,16 +1068,35 @@ static STUB_DATA struct NotifierBlock g_gdbNotifier = { .priority = 999, }; -STUB_TEXT int OsGdbStubInit(void) +STUB_TEXT int OsGdbStubEarlyInit(void) { + GdbRegHandlers(); OsGdbConfigInitMemRegions(); if (OsGdbRingBufferInit()) { return -1; } - OsRegisterDieNotifier(&g_gdbNotifier); +#ifdef OS_OPTION_SMP + OsGdbStubSmpInit(); +#endif OsGdbArchInit(); - - return 0; } +#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 diff --git a/src/component/gdbstub/include/arch_interface.h b/src/component/gdbstub/include/arch_interface.h index 88a077cf9a46d07e4f89718cdf3dbaad14f809af..1014c832827237a71c6a0bfdca68f4d21541fffc 100644 --- a/src/component/gdbstub/include/arch_interface.h +++ b/src/component/gdbstub/include/arch_interface.h @@ -12,13 +12,13 @@ extern void OsGdbArchInit(void); * * Continue software execution. */ -extern void OsGdbArchContinue(void); +extern int OsGdbArchContinue(U32 cid); /** * * Continue software execution until reaches the next statement. */ -extern void OsGdbArchStep(void); +extern int OsGdbArchStep(U32 cid); /** * Read all registers, and outputs as hexadecimal string. @@ -26,6 +26,7 @@ extern void OsGdbArchStep(void); * This reads all CPU registers and outputs as hexadecimal string. * The output string must be parsable by GDB. * + * @param cid Core Id * @param buf Buffer to output hexadecimal string. * @param buflen Length of buffer. * @@ -33,7 +34,7 @@ extern void OsGdbArchStep(void); * Return 0 if error or not supported. */ -extern int OsGdbArchReadAllRegs(U8 *buf, int buflen); +extern int OsGdbArchReadAllRegs(U32 cid, U8 *buf, int buflen); /** * Take a hexadecimal string and update all registers. @@ -41,23 +42,24 @@ extern int OsGdbArchReadAllRegs(U8 *buf, int buflen); * This takes in a hexadecimal string as presented from GDB, * and updates all CPU registers with new values. * + * @param cid Core Id * @param hex Input hexadecimal string. * @param hexlen Length of hexadecimal string. * * @return Length of hexadecimal string parsed. * Return 0 if error or not supported. */ -extern int OsGdbArchWriteAllRegs(U8 *hex, int hexlen); +extern int OsGdbArchWriteAllRegs(U32 cid, U8 *hex, int hexlen); -extern int OsGdbArchReadReg(U32 regno, U8 *buf, int buflen); +extern int OsGdbArchReadReg(U32 cid, U32 regno, U8 *buf, int buflen); -extern int OsGdbArchWriteReg(U32 regno, U8 *buf, int buflen); +extern int OsGdbArchWriteReg(U32 cid, U32 regno, U8 *buf, int buflen); extern int OsGdbArchRemoveSwBkpt(struct GdbBkpt *bkpt); extern int OsGdbArchSetSwBkpt(struct GdbBkpt *bkpt); -extern void OsGdbArchPrepare(void *stk); +extern int OsGdbArchPrepare(void *stk); extern void OsGdbArchFinish(void *stk); @@ -81,7 +83,7 @@ extern int OsGdbArchSetHwBkpt(uintptr_t addr, int len, enum GdbBkptType bptype); /** * Allow an architecture to specify how to disable hardware breakpoints for a single cpu. */ -extern void OsGdbArchDisableHwBkpts(); +extern void OsGdbArchDisableHwBkpts(void); /** * Allow an architecture to specify how to remove all hardware breakpoints. @@ -92,6 +94,9 @@ extern int OsGdbArchHitHwBkpt(uintptr_t *addr, unsigned *type); extern int OsGdbArchNotifyDie(int action, void *data); -extern int OsGdbGetStopReason(); +extern int OsGdbGetStopReason(void); +extern void OsGdbSmpArchInit(void); + +extern void OsGdbArchForceStep(U32 cid, bool slaveFlg); #endif /* _ARCH_INTERFACE_H_ */ \ No newline at end of file diff --git a/src/component/gdbstub/include/gdbstub_common.h b/src/component/gdbstub/include/gdbstub_common.h index 0d28367c01a02a6a3bdf46f1e1d9e0caf2b3d920..9709618dc5b2c4fbed32574a2d92e5aa49e221e5 100644 --- a/src/component/gdbstub/include/gdbstub_common.h +++ b/src/component/gdbstub/include/gdbstub_common.h @@ -67,5 +67,10 @@ static inline const char *GetWatchTypeStr(unsigned type) } } +/* Exception state values */ +#define DCPU_WANT_MASTER 0x1 /* Waiting to become a master kgdb cpu */ +#define DCPU_NEXT_MASTER 0x2 /* Transition from one master cpu to another */ +#define DCPU_IS_SLAVE 0x4 /* Slave cpu enter exception */ + extern STUB_TEXT void OsGdbHandleException(void *stk); #endif /* _GDBSTUB_COMMON_H_ */