This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new ee5d5a8080 arch/x86_64: add debug interface ee5d5a8080 is described below commit ee5d5a80803c98f7967cb6378acb09fa9967a5d1 Author: p-szafonimateusz <p-szafonimate...@xiaomi.com> AuthorDate: Mon Dec 2 10:35:28 2024 +0100 arch/x86_64: add debug interface add hardware debug interface for x86_64 which can be used with gdbstub Signed-off-by: p-szafonimateusz <p-szafonimate...@xiaomi.com> --- arch/x86_64/Kconfig | 1 + arch/x86_64/src/common/CMakeLists.txt | 4 + arch/x86_64/src/common/Make.defs | 4 + arch/x86_64/src/common/x86_64_hwdebug.c | 531 +++++++++++++++++++++++++++++ arch/x86_64/src/common/x86_64_hwdebug.h | 48 +++ arch/x86_64/src/common/x86_64_initialize.c | 10 + arch/x86_64/src/intel64/intel64_cpustart.c | 7 + 7 files changed, 605 insertions(+) diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index ae6b9b89c1..528fc53117 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -39,6 +39,7 @@ config ARCH_INTEL64 select ARCH_ICACHE select ARCH_DCACHE select ARCH_HAVE_IRQTRIGGER + select ARCH_HAVE_DEBUG ---help--- Intel x86_64 architecture diff --git a/arch/x86_64/src/common/CMakeLists.txt b/arch/x86_64/src/common/CMakeLists.txt index 2bc38cb7f6..5186b6cd10 100644 --- a/arch/x86_64/src/common/CMakeLists.txt +++ b/arch/x86_64/src/common/CMakeLists.txt @@ -80,4 +80,8 @@ if(NOT CONFIG_BUILD_FLAT) x86_64_signal_dispatch.c) endif() +if(CONFIG_ARCH_HAVE_DEBUG) + list(APPEND SRCS x86_64_hwdebug.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/x86_64/src/common/Make.defs b/arch/x86_64/src/common/Make.defs index 88377c90a6..aadf928214 100644 --- a/arch/x86_64/src/common/Make.defs +++ b/arch/x86_64/src/common/Make.defs @@ -72,3 +72,7 @@ endif ifndef CONFIG_ALARM_ARCH CMN_CSRCS += x86_64_udelay.c x86_64_mdelay.c endif + +ifeq ($(CONFIG_ARCH_HAVE_DEBUG),y) +CMN_CSRCS += x86_64_hwdebug.c +endif diff --git a/arch/x86_64/src/common/x86_64_hwdebug.c b/arch/x86_64/src/common/x86_64_hwdebug.c new file mode 100644 index 0000000000..f849b689ed --- /dev/null +++ b/arch/x86_64/src/common/x86_64_hwdebug.c @@ -0,0 +1,531 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_hwdebug.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/gdbstub.h> + +#include "sched/sched.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define X86_64_HWBRKP_COUNT (4) + +/* Conditions for DRx */ + +#define X86_64_DR7_RW_BREAK (0) +#define X86_64_DR7_RW_WATCH_W (1) +#define X86_64_DR7_RW_WATCH_IO_RW (2) +#define X86_64_DR7_RW_WATCH_RW (3) + +/* Size of DRx */ + +#define X86_64_DR7_LEN_1B (0) +#define X86_64_DR7_LEN_2B (1) +#define X86_64_DR7_LEN_8B (2) +#define X86_64_DR7_LEN_4B (3) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* DR7 register */ + +struct x86_64_dr7_reg_s +{ + uint64_t l0:1; /* Bit 0: Local DR0 breakpoint */ + uint64_t g0:1; /* Bit 1: Global DR0 breakpoint */ + uint64_t l1:1; /* Bit 2: Local DR1 breakpoint */ + uint64_t g1:1; /* Bit 3: Global DR1 breakpoint */ + uint64_t l2:1; /* Bit 4: Local DR2 breakpoint */ + uint64_t g2:1; /* Bit 5: Global DR2 breakpoint */ + uint64_t l3:1; /* Bit 6: Local DR3 breakpoint */ + uint64_t g3:1; /* Bit 7: Global DR3 breakpoint */ + uint64_t le:1; /* Bit 8: Local Exact Breakpoint enable */ + uint64_t ge:1; /* Bit 9: Global Extract Breakpoint enable */ + uint64_t _zeros0:6; /* Bits 10-15: Zeros */ + uint64_t rw0:2; /* Bits 16-17: Conditions for DR0 */ + uint64_t len0:2; /* Bits 18-19: Size of DR0 breakpoint */ + uint64_t rw1:2; /* Bits 20-21: Conditions for DR1 */ + uint64_t len1:2; /* Bits 22-23: Size of DR1 breakpoint */ + uint64_t rw2:2; /* Bits 24-25: Conditions for DR2 */ + uint64_t len2:2; /* Bits 26:27: Size of DR2 breakpoint */ + uint64_t rw3:2; /* Bits 28-29: Conditions for DR3 */ + uint64_t len3:2; /* Bits 30-31: Size of DR3 breakpoint */ + uint64_t _zeros1:32; /* Bits 32-64: Zeros */ +}; + +union x86_64_dr7_reg_u +{ + struct x86_64_dr7_reg_s s; + uint64_t u64; +}; + +/* DR6 register */ + +struct x86_64_dr6_reg_s +{ + uint64_t b0:1; /* Bit 0: BRK0 set */ + uint64_t b1:1; /* Bit 1: BRK1 set */ + uint64_t b2:1; /* Bit 2: BRK2 set */ + uint64_t b3:1; /* Bit 3: BRK3 set */ + uint64_t _zeros0:9; /* Bits 4-12: Zeros */ + uint64_t bd:1; /* Bit 13: Debug Register Address Detected */ + uint64_t bs:1; /* Bit 14: Singe-Step execution */ + uint64_t bt:1; /* Bit 15: Task Switch breakpoint */ + uint64_t _zeros1:48; /* Bits 16-64: Zeros */ +}; + +union x86_64_dr6_reg_u +{ + struct x86_64_dr6_reg_s s; + uint64_t u64; +}; + +/* Debug trigger */ + +struct x86_64_debug_trigger_s +{ + bool used; /* Trigger is used */ + int type; /* Trigger type */ + void *address; /* Trigger address */ + size_t size; /* Trigger region size */ + debug_callback_t callback; /* Debug callback */ + void *arg; /* Debug callback argument */ +}; + +/* Debug context */ + +struct x86_64_debug_ctx_s +{ + struct x86_64_debug_trigger_s trigger[X86_64_HWBRKP_COUNT]; + struct x86_64_debug_trigger_s step; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Save the trigger address info */ + +static struct x86_64_debug_ctx_s g_dbg_ctx[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: get_drX() / set_drX() + ****************************************************************************/ + +static inline void set_dr0(uint64_t dr0) +{ + __asm__ volatile("\tmov %0, %%dr0" :: "r"(dr0)); +} + +static inline uint64_t get_dr0(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr0, %0\n" : "=r" (regval)); + return regval; +} + +static inline void set_dr1(uint64_t dr1) +{ + __asm__ volatile("\tmov %0, %%dr1" :: "r"(dr1)); +} + +static inline uint64_t get_dr1(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr1, %0\n" : "=r" (regval)); + return regval; +} + +static inline void set_dr2(uint64_t dr2) +{ + __asm__ volatile("\tmov %0, %%dr2" :: "r"(dr2)); +} + +static inline uint64_t get_dr2(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr2, %0\n" : "=r" (regval)); + return regval; +} + +static inline void set_dr3(uint64_t dr3) +{ + __asm__ volatile("\tmov %0, %%dr3" :: "r"(dr3)); +} + +static inline uint64_t get_dr3(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr3, %0\n" : "=r" (regval)); + return regval; +} + +static inline void set_dr6(uint64_t dr6) +{ + __asm__ volatile("\tmov %0, %%dr6" :: "r"(dr6)); +} + +static inline uint64_t get_dr6(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr6, %0\n" : "=r" (regval)); + return regval; +} + +static inline void set_dr7(uint64_t dr7) +{ + __asm__ volatile("\tmov %0, %%dr7" :: "r"(dr7)); +} + +static inline uint64_t get_dr7(void) +{ + uint64_t regval; + __asm__ volatile("\tmov %%dr7, %0\n" : "=r" (regval)); + return regval; +} + +/**************************************************************************** + * Name: x86_64_debug_step + ****************************************************************************/ + +static void x86_64_debug_step(bool enable) +{ + uint64_t *regs = g_running_tasks[this_cpu()]->xcp.regs; + + /* Reset or set Trap flag */ + + if (enable) + { + regs[REG_RFLAGS] |= X86_64_RFLAGS_TF; + } + else + { + regs[REG_RFLAGS] &= ~X86_64_RFLAGS_TF; + } + + /* Request full context switch so we update RFLAGS */ + + regs[REG_AUX] |= REG_AUX_FULLCONTEXT; +} + +/**************************************************************************** + * Name: x86_64_set_dr + ****************************************************************************/ + +static void x86_64_set_dr(uint8_t i, uint8_t g, uint8_t rw, uint8_t len, + uint64_t addr) +{ + union x86_64_dr7_reg_u dr7; + + /* Get DR7 */ + + dr7.u64 = get_dr7(); + + switch (i) + { + case 0: + { + set_dr0(addr); + dr7.s.g0 = 1; + dr7.s.rw0 = rw; + dr7.s.len0 = len; + break; + } + + case 1: + { + set_dr1(addr); + dr7.s.g1 = 1; + dr7.s.rw1 = rw; + dr7.s.len1 = len; + break; + } + + case 2: + { + set_dr2(addr); + dr7.s.g2 = 1; + dr7.s.rw2 = rw; + dr7.s.len2 = len; + break; + } + + case 3: + { + set_dr3(addr); + dr7.s.g3 = 1; + dr7.s.rw3 = rw; + dr7.s.len3 = len; + break; + } + + default: + { + _err("unsuported DR %d\n", i); + PANIC(); + } + } + + /* Update DR7 */ + + set_dr7(dr7.u64); +} + +/**************************************************************************** + * Name: x86_64_debug_handler + ****************************************************************************/ + +static int x86_64_debug_handler(int irq, void *c, void *arg) +{ + uint8_t cpu = this_cpu(); + union x86_64_dr6_reg_u dr6; + int i; + + dr6.u64 = get_dr6(); + + /* Single step execution */ + + if (dr6.s.bs) + { + dr6.s.bs = 0; + set_dr6(dr6.u64); + + /* Free step trigger */ + + g_dbg_ctx[cpu].step.used = false; + + /* Call the step callback */ + + g_dbg_ctx[cpu].step.callback( + g_dbg_ctx[cpu].step.type, + g_dbg_ctx[cpu].step.address, + g_dbg_ctx[cpu].step.size, + g_dbg_ctx[cpu].step.arg); + + return 0; + } + + /* Get the trigger index */ + + for (i = 0; i < X86_64_HWBRKP_COUNT; i++) + { + if (dr6.u64 & (1 << i)) + { + /* Clear flag */ + + dr6.u64 &= ~(1 << i); + + /* Call the trigger callback */ + + if (g_dbg_ctx[cpu].trigger[i].callback) + { + g_dbg_ctx[cpu].trigger[i].callback( + g_dbg_ctx[cpu].trigger[i].type, + g_dbg_ctx[cpu].trigger[i].address, + g_dbg_ctx[cpu].trigger[i].size, + g_dbg_ctx[cpu].trigger[i].arg); + } + } + } + + /* Set DR6 */ + + set_dr6(dr6.u64); + + /* Set Resume flag so we don't stuck in debug handler */ + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_debugpoint_add + ****************************************************************************/ + +int up_debugpoint_add(int type, void *addr, size_t size, + debug_callback_t callback, void *arg) +{ + uint8_t cpu = this_cpu(); + uint8_t rw; + int i; + + /* Get type */ + + switch (type) + { + case GDB_STOPREASON_BREAKPOINT: + case GDB_STOPREASON_STEPPOINT: + { + rw = X86_64_DR7_RW_BREAK; + break; + } + + case GDB_STOPREASON_WATCHPOINT_WO: + { + rw = X86_64_DR7_RW_WATCH_W; + break; + } + + case GDB_STOPREASON_WATCHPOINT_RW: + { + rw = X86_64_DR7_RW_WATCH_RW; + break; + } + + default: + { + _err("unsuported debugpoint type %d\n", type); + PANIC(); + } + } + + /* Single step execution */ + + if (type == GDB_STOPREASON_STEPPOINT) + { + if (g_dbg_ctx[cpu].step.used) + { + return -EBUSY; + } + + g_dbg_ctx[cpu].step.used = true; + g_dbg_ctx[cpu].step.type = type; + g_dbg_ctx[cpu].step.address = addr; + g_dbg_ctx[cpu].step.size = size; + g_dbg_ctx[cpu].step.callback = callback; + g_dbg_ctx[cpu].step.arg = arg; + + /* Enable step mode */ + + x86_64_debug_step(true); + + return OK; + } + + /* Hardware triggers */ + + for (i = 0; i < X86_64_HWBRKP_COUNT; i++) + { + if (!g_dbg_ctx[cpu].trigger[i].used) + { + /* Update local table */ + + g_dbg_ctx[cpu].trigger[i].used = true; + g_dbg_ctx[cpu].trigger[i].type = type; + g_dbg_ctx[cpu].trigger[i].address = addr; + g_dbg_ctx[cpu].trigger[i].size = size; + g_dbg_ctx[cpu].trigger[i].callback = callback; + g_dbg_ctx[cpu].trigger[i].arg = arg; + + /* Configure DR */ + + x86_64_set_dr(i, 1, rw, X86_64_DR7_LEN_1B, (uintptr_t)addr); + + return OK; + } + } + + return -ENOSPC; +} + +/**************************************************************************** + * Name: up_debugpoint_remove + ****************************************************************************/ + +int up_debugpoint_remove(int type, void *addr, size_t size) +{ + uint8_t cpu = this_cpu(); + int i; + + /* Single step execution */ + + if (type == GDB_STOPREASON_STEPPOINT) + { + g_dbg_ctx[cpu].step.used = false; + + /* Disable step mode */ + + x86_64_debug_step(false); + + return OK; + } + + /* Hardware triggers */ + + for (i = 0; i < X86_64_HWBRKP_COUNT; i++) + { + if (g_dbg_ctx[cpu].trigger[i].address == addr) + { + /* Mark as free */ + + g_dbg_ctx[cpu].trigger[i].used = false; + + /* Clear DR */ + + x86_64_set_dr(i, 0, 0, 0, 0); + + return OK; + } + } + + return -ENOENT; +} + +/**************************************************************************** + * Name: x86_64_hwdebug_init + * + * Description: + * One-time initializtion for hardware debug interface. + * + ****************************************************************************/ + +void x86_64_hwdebug_init(void) +{ + /* Attach debug interrupt and breakpoint interrupt */ + + irq_attach(ISR1, x86_64_debug_handler, NULL); + irq_attach(ISR3, x86_64_debug_handler, NULL); + + /* Disable all breakpoints */ + + set_dr7(0); + set_dr0(0); + set_dr1(0); + set_dr2(0); + set_dr3(0); +} diff --git a/arch/x86_64/src/common/x86_64_hwdebug.h b/arch/x86_64/src/common/x86_64_hwdebug.h new file mode 100644 index 0000000000..e128f08048 --- /dev/null +++ b/arch/x86_64/src/common/x86_64_hwdebug.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_hwdebug.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_X86_64_SRC_COMMON_X86_64_HWDEBUG_H +#define __ARCH_X86_64_SRC_COMMON_X86_64_HWDEBUG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: x86_64_hwdebug_init + * + * Description: + * One-time initializtion for hardware debug interface. + * + ****************************************************************************/ + +void x86_64_hwdebug_init(void); + +#endif /* __ARCH_X86_64_SRC_COMMON_X86_64_HWDEBUG_H */ diff --git a/arch/x86_64/src/common/x86_64_initialize.c b/arch/x86_64/src/common/x86_64_initialize.c index 10f67746b6..06520dcb12 100644 --- a/arch/x86_64/src/common/x86_64_initialize.c +++ b/arch/x86_64/src/common/x86_64_initialize.c @@ -32,6 +32,10 @@ # include <nuttx/drivers/addrenv.h> #endif +#ifdef CONFIG_ARCH_HAVE_DEBUG +# include "x86_64_hwdebug.h" +#endif + #include <arch/acpi.h> #include "x86_64_internal.h" @@ -135,6 +139,12 @@ void up_initialize(void) } #endif +#ifdef CONFIG_ARCH_HAVE_DEBUG + /* Initialize hardware debug interface */ + + x86_64_hwdebug_init(); +#endif + /* Initialize the serial device driver */ #ifdef USE_SERIALDRIVER diff --git a/arch/x86_64/src/intel64/intel64_cpustart.c b/arch/x86_64/src/intel64/intel64_cpustart.c index c7c6a83493..81ce6af6e4 100644 --- a/arch/x86_64/src/intel64/intel64_cpustart.c +++ b/arch/x86_64/src/intel64/intel64_cpustart.c @@ -39,6 +39,7 @@ #include "intel64_lowsetup.h" #include "intel64_cpu.h" +#include "x86_64_hwdebug.h" /**************************************************************************** * Private Types @@ -192,6 +193,12 @@ void x86_64_ap_boot(void) __revoke_low_memory(); } +#ifdef CONFIG_ARCH_HAVE_DEBUG + /* Initialize hardware debug interface */ + + x86_64_hwdebug_init(); +#endif + up_update_task(tcb); /* Then transfer control to the IDLE task */