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
commit 1df6aa92f9da4930e651658b17b068c44e3a0da2 Author: wangmingrong1 <wangmingro...@xiaomi.com> AuthorDate: Tue Jun 24 16:59:32 2025 +0800 arm64: Support hardware debug Signed-off-by: wangmingrong1 <wangmingro...@xiaomi.com> --- arch/arm64/Kconfig | 1 + arch/arm64/src/common/CMakeLists.txt | 4 + arch/arm64/src/common/Make.defs | 4 + arch/arm64/src/common/arm64_hwdebug.c | 460 +++++++++++++++++++++++++++++++ arch/arm64/src/common/arm64_hwdebug.h | 124 +++++++++ arch/arm64/src/common/arm64_initialize.c | 4 + arch/arm64/src/common/arm64_internal.h | 4 + 7 files changed, 601 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index f3fb57f077..016809c4e0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -343,6 +343,7 @@ config ARCH_CPU_UNKNOWN default n select ARCH_ARMV8A select ARCH_HAVE_TRUSTZONE + select ARCH_HAVE_DEBUG select ARCH_DCACHE select ARCH_ICACHE select ARCH_HAVE_MMU diff --git a/arch/arm64/src/common/CMakeLists.txt b/arch/arm64/src/common/CMakeLists.txt index e704078ed4..4a8c826755 100644 --- a/arch/arm64/src/common/CMakeLists.txt +++ b/arch/arm64/src/common/CMakeLists.txt @@ -59,6 +59,10 @@ if(CONFIG_ARM64_GIC_VERSION EQUAL 2) endif() endif() +if(CONFIG_ARCH_HAVE_DEBUG) + list(APPEND SRCS arm64_hwdebug.c) +endif() + if(CONFIG_ARCH_HAVE_EL3) list(APPEND SRCS arm64_smccc.S) endif() diff --git a/arch/arm64/src/common/Make.defs b/arch/arm64/src/common/Make.defs index 3c1dacb3d0..8a6328af1e 100644 --- a/arch/arm64/src/common/Make.defs +++ b/arch/arm64/src/common/Make.defs @@ -67,6 +67,10 @@ CMN_CSRCS += arm64_gicv2m.c endif endif +ifneq ($(CONFIG_ARCH_HAVE_DEBUG),) +CMN_CSRCS += arm64_hwdebug.c +endif + ifeq ($(CONFIG_ARCH_HAVE_EL3),y) CMN_ASRCS += arm64_smccc.S endif diff --git a/arch/arm64/src/common/arm64_hwdebug.c b/arch/arm64/src/common/arm64_hwdebug.c new file mode 100644 index 0000000000..9343776a0b --- /dev/null +++ b/arch/arm64/src/common/arm64_hwdebug.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * arch/arm64/src/common/arm64_hwdebug.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <nuttx/arch.h> +#include <nuttx/mm/kasan.h> + +#include <stdint.h> +#include <assert.h> +#include <debug.h> + +#include "arm64_fatal.h" +#include "arm64_hwdebug.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ARM64_DBGBWCR_VAL(type, len) \ + (arm64_convert_type(type) | arm64_convert_size(len) | \ + ARM64_DBGBCR_PAC_ALL | ARM64_DBGBWCR_E) + +#define ARM64_MASK_ADDR(addr) \ + ((uint64_t)kasan_clear_tag((void *)addr) & ~0x3) + +/**************************************************************************** + * Private Type + ****************************************************************************/ + +struct arm64_debugpoint_s +{ + int type; + void *addr; + size_t size; + debug_callback_t callback; + void *arg; +}; + +struct arm64_debug_s +{ + /* Breakpoint currently in use for each BRP, WRP */ + + struct arm64_debugpoint_s brps[ID_AA64DFR0_MAX_BRPS]; + struct arm64_debugpoint_s wrps[ID_AA64DFR0_MAX_WRPS]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct arm64_debug_s g_arm64_debug[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * Private Function + ****************************************************************************/ + +static uint32_t arm64_convert_type(int type) +{ + switch (type) + { + case DEBUGPOINT_WATCHPOINT_RO: + return ARM64_DBGBWCR_LSC_LOAD << ARM64_DBGBWCR_LSC_OFFSET; + + case DEBUGPOINT_WATCHPOINT_WO: + return ARM64_DBGBWCR_LSC_STORE << ARM64_DBGBWCR_LSC_OFFSET; + + case DEBUGPOINT_WATCHPOINT_RW: + return (ARM64_DBGBWCR_LSC_LOAD | ARM64_DBGBWCR_LSC_STORE) + << ARM64_DBGBWCR_LSC_OFFSET; + + case DEBUGPOINT_BREAKPOINT: + case DEBUGPOINT_STEPPOINT: + default: + return ARM64_DBGBWCR_LSC_EXECUTE << ARM64_DBGBWCR_LSC_OFFSET; + } +} + +static uint32_t arm64_convert_size(size_t len) +{ + switch (len) + { + case 1: + return ARM64_DBGBWCR_BAS_LEN_1 << ARM64_DBGBWCR_BAS_OFFSET; + case 2: + return ARM64_DBGBWCR_BAS_LEN_2 << ARM64_DBGBWCR_BAS_OFFSET; + case 3: + return ARM64_DBGBWCR_BAS_LEN_3 << ARM64_DBGBWCR_BAS_OFFSET; + case 4: + return ARM64_DBGBWCR_BAS_LEN_4 << ARM64_DBGBWCR_BAS_OFFSET; + case 5: + return ARM64_DBGBWCR_BAS_LEN_5 << ARM64_DBGBWCR_BAS_OFFSET; + case 6: + return ARM64_DBGBWCR_BAS_LEN_6 << ARM64_DBGBWCR_BAS_OFFSET; + case 7: + return ARM64_DBGBWCR_BAS_LEN_7 << ARM64_DBGBWCR_BAS_OFFSET; + case 8: + default: + return ARM64_DBGBWCR_BAS_LEN_8 << ARM64_DBGBWCR_BAS_OFFSET; + } +} + +/* Determine number of usable WRPs available. */ + +static int arm64_get_num_wrps(void) +{ + return (read_sysreg(id_aa64dfr0_el1) >> ID_AA64DFR0_EL1_WRPS_OFFSET) + & ID_AA64DFR0_EL1_WRPS_MASK; +} + +/* Determine number of usable BRPs available. */ + +static int arm64_get_num_brps(void) +{ + return (read_sysreg(id_aa64dfr0_el1) >> ID_AA64DFR0_EL1_BRPS_OFFSET) + & ID_AA64DFR0_EL1_BRPS_MASK; +} + +/**************************************************************************** + * Name: arm64_watchpoint_add + * + * Description: + * Add a watchpoint on the address. + * + * Input Parameters: + * type - The type of the watchpoint + * addr - The address to be watched + * size - The size of the address to be watched + * + * Returned Value: + * Index in wprs array on success; a negated errno value on failure + * + * Notes: + * The size of the watchpoint is determined by the hardware. + * + ****************************************************************************/ + +static int arm64_watchpoint_add(int type, uint64_t addr, size_t size) +{ + int num = arm64_get_num_wrps(); + int i; + + for (i = 0; i < num; i++) + { + if (!(ARM64_DBG_GETN(wcr, i) & ARM64_DBGBWCR_E)) + { + ARM64_DBG_SETN(wvr, i, ARM64_MASK_ADDR(addr)); + ARM64_DBG_SETN(wcr, i, ARM64_DBGBWCR_VAL(type, size)); + return i; + } + } + + return -ENOSPC; +} + +/**************************************************************************** + * Name: arm64_watchpoint_remove + * + * Description: + * Remove a watchpoint on the address. + * + * Input Parameters: + * addr - The address to be watched. + * + * Returned Value: + * Index in wprs array on success; a negated errno value on failure + * + ****************************************************************************/ + +static int arm64_watchpoint_remove(uint64_t addr) +{ + int num = arm64_get_num_wrps(); + int i; + + for (i = 0; i < num; i++) + { + if (ARM64_DBG_GETN(wvr, i) == ARM64_MASK_ADDR(addr)) + { + ARM64_DBG_SETN(wcr, i, 0); + return i; + } + } + + return -ENOENT; +} + +/**************************************************************************** + * Name: arm64_breakpoint_add + * + * Description: + * Add a breakpoint on addr. + * + * Input Parameters: + * addr - The address to break. + * + * Returned Value: + * Index in bprs array on success; a negated errno value on failure + * + * Notes: + * 1. If breakpoint is already set, it will do nothing. + * 2. If all comparators are in use, it will return -1. + * 3. When the breakpoint trigger, if enable monitor exception already , + * will cause a debug monitor exception, oaddr=0x4020392ctherwise will + * cause a hard fault. + * + ****************************************************************************/ + +static int arm64_breakpoint_add(uintptr_t addr) +{ + int num = arm64_get_num_brps(); + int i; + + for (i = 0; i < num; i++) + { + if (!(ARM64_DBG_GETN(bcr, i) & ARM64_DBGBWCR_E)) + { + ARM64_DBG_SETN(bvr, i, ARM64_MASK_ADDR(addr)); + ARM64_DBG_SETN(bcr, i, + ARM64_DBGBWCR_VAL(DEBUGPOINT_BREAKPOINT, 8)); + return i; + } + } + + return -ENOSPC; +} + +/**************************************************************************** + * Name: arm64_breakpoint_remove + * + * Description: + * Remove a breakpoint on addr. + * + * Input Parameters: + * addr - The address to remove. + * + * Returned Value: + * Index in bprs array on success; a negated errno value on failure + * + ****************************************************************************/ + +static int arm64_breakpoint_remove(uintptr_t addr) +{ + int num = arm64_get_num_brps(); + int i; + + for (i = 0; i < num; i++) + { + if (ARM64_DBG_GETN(bvr, i) == ARM64_MASK_ADDR(addr)) + { + ARM64_DBG_SETN(bcr, i, 0); + return i; + } + } + + return -ENOENT; +} + +static int arm64_watchpoint_match(uint64_t *regs, uint64_t far, uint64_t esr) +{ + struct arm64_debugpoint_s *dp = g_arm64_debug[this_cpu()].wrps; + int num = arm64_get_num_wrps(); + int i; + + for (i = 0; i < num; i++) + { + if (ARM64_MASK_ADDR(dp[i].addr) == ARM64_MASK_ADDR(far)) + { + dp[i].callback(dp[i].type, dp[i].addr, + dp[i].size, dp[i].arg); + return OK; + } + } + + return EFAULT; +} + +static int arm64_breakpoint_match(uint64_t *regs, uint64_t far, uint64_t esr) +{ + struct arm64_debugpoint_s *dp = g_arm64_debug[this_cpu()].brps; + int num = arm64_get_num_brps(); + int i; + + for (i = 0; i < num; i++) + { + if (ARM64_MASK_ADDR(dp[i].addr) == ARM64_MASK_ADDR(up_getusrpc(regs))) + { + dp[i].callback(dp[i].type, dp[i].addr, + dp[i].size, dp[i].arg); + return OK; + } + } + + return EFAULT; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm64_enable_dbgmonitor + * + * Description: + * This function enables the debug monitor exception. + * + ****************************************************************************/ + +int arm64_enable_dbgmonitor(void) +{ + uint32_t mdscr; + + /* Determine how many BRPs/WRPs are available. + * And work out the maximum supported watchpoint length. + */ + + binfo("found %d breakpoint and %d watchpoint registers.\n", + arm64_get_num_brps(), arm64_get_num_wrps()); + + mdscr = read_sysreg(mdscr_el1); + mdscr |= ARM64_MDSCR_EL1_MDE | ARM64_MDSCR_EL1_KDE; + write_sysreg(mdscr, mdscr_el1); + + arm64_register_debug_hook(DBG_ESR_EVT_HWBP, arm64_breakpoint_match); + arm64_register_debug_hook(DBG_ESR_EVT_HWWP, arm64_watchpoint_match); + return OK; +} + +/**************************************************************************** + * Name: up_debugpoint_add + * + * Description: + * Add a debugpoint. + * + * Input Parameters: + * type - The debugpoint type. optional value: + * DEBUGPOINT_WATCHPOINT_RO - Read only watchpoint. + * DEBUGPOINT_WATCHPOINT_WO - Write only watchpoint. + * DEBUGPOINT_WATCHPOINT_RW - Read and write watchpoint. + * DEBUGPOINT_BREAKPOINT - Breakpoint. + * DEBUGPOINT_STEPPOINT - Single step. + * addr - The address to be debugged. + * size - The watchpoint size. only for watchpoint. + * callback - The callback function when debugpoint triggered. + * if NULL, the debugpoint will be removed. + * arg - The argument of callback function. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int up_debugpoint_add(int type, void *addr, size_t size, + debug_callback_t callback, void *arg) +{ + struct arm64_debugpoint_s *dp; + int cpu = this_cpu(); + int ret = -EINVAL; + + if (type == DEBUGPOINT_BREAKPOINT) + { + ret = arm64_breakpoint_add((uintptr_t)addr); + dp = g_arm64_debug[cpu].brps; + } + else if (type == DEBUGPOINT_WATCHPOINT_RO || + type == DEBUGPOINT_WATCHPOINT_WO || + type == DEBUGPOINT_WATCHPOINT_RW) + { + ret = arm64_watchpoint_add(type, (uintptr_t)addr, size); + dp = g_arm64_debug[cpu].wrps; + } + + if (ret < 0) + { + return ret; + } + + dp[ret].type = type; + dp[ret].addr = addr; + dp[ret].size = size; + dp[ret].callback = callback; + dp[ret].arg = arg; + + return OK; +} + +/**************************************************************************** + * Name: up_debugpoint_remove + * + * Description: + * Remove a debugpoint. + * + * Input Parameters: + * type - The debugpoint type. optional value: + * DEBUGPOINT_WATCHPOINT_RO - Read only watchpoint. + * DEBUGPOINT_WATCHPOINT_WO - Write only watchpoint. + * DEBUGPOINT_WATCHPOINT_RW - Read and write watchpoint. + * DEBUGPOINT_BREAKPOINT - Breakpoint. + * DEBUGPOINT_STEPPOINT - Single step. + * addr - The address to be debugged. + * size - The watchpoint size. only for watchpoint. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int up_debugpoint_remove(int type, void *addr, size_t size) +{ + struct arm64_debugpoint_s *dp; + int cpu = this_cpu(); + int ret = -EINVAL; + + if (type == DEBUGPOINT_BREAKPOINT) + { + ret = arm64_breakpoint_remove((uintptr_t)addr); + dp = g_arm64_debug[cpu].brps; + } + else if (type == DEBUGPOINT_WATCHPOINT_RO || + type == DEBUGPOINT_WATCHPOINT_WO || + type == DEBUGPOINT_WATCHPOINT_RW) + { + ret = arm64_watchpoint_remove((uintptr_t)addr); + dp = g_arm64_debug[cpu].wrps; + } + + if (ret < 0) + { + return ret; + } + + dp[ret].type = 0; + dp[ret].addr = 0; + dp[ret].size = 0; + dp[ret].callback = NULL; + dp[ret].arg = NULL; + + return OK; +} diff --git a/arch/arm64/src/common/arm64_hwdebug.h b/arch/arm64/src/common/arm64_hwdebug.h new file mode 100644 index 0000000000..0efabee14e --- /dev/null +++ b/arch/arm64/src/common/arm64_hwdebug.h @@ -0,0 +1,124 @@ +/**************************************************************************** + * arch/arm64/src/common/arm64_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_ARM64_SRC_COMMON_ARM64_HWDEBUG_H +#define __ARCH_ARM64_SRC_COMMON_ARM64_HWDEBUG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/bits.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ARM64_DBGBWCR_E BIT(0) + +#define ARM64_DBGBWCR_LSC_OFFSET 3 +#define ARM64_DBGBWCR_LSC_EXECUTE 0 +#define ARM64_DBGBWCR_LSC_LOAD 1 +#define ARM64_DBGBWCR_LSC_STORE 2 + +#define ARM64_DBGBWCR_BAS_OFFSET 5 +#define ARM64_DBGBWCR_BAS_LEN_1 0x1 +#define ARM64_DBGBWCR_BAS_LEN_2 0x3 +#define ARM64_DBGBWCR_BAS_LEN_3 0x7 +#define ARM64_DBGBWCR_BAS_LEN_4 0xf +#define ARM64_DBGBWCR_BAS_LEN_5 0x1f +#define ARM64_DBGBWCR_BAS_LEN_6 0x3f +#define ARM64_DBGBWCR_BAS_LEN_7 0x7f +#define ARM64_DBGBWCR_BAS_LEN_8 0xff + +#define ARM64_DBGBCR_PAC_PRIV BIT(1) +#define ARM64_DBGBCR_PAC_USER BIT(2) +#define ARM64_DBGBCR_PAC_ALL (ARM64_DBGBCR_PAC_PRIV | ARM64_DBGBCR_PAC_USER) + +#define ARM64_MDSCR_EL1_KDE BIT(13) +#define ARM64_MDSCR_EL1_MDE BIT(15) + +#define ID_AA64DFR0_EL1_BRPS_MASK 0xf +#define ID_AA64DFR0_EL1_BRPS_OFFSET 12 + +#define ID_AA64DFR0_EL1_WRPS_MASK 0xf +#define ID_AA64DFR0_EL1_WRPS_OFFSET 20 + +#define ID_AA64DFR0_MAX_BRPS 16 +#define ID_AA64DFR0_MAX_WRPS 16 + +#define ARM64_DBG_SET_CASE(reg, n, val) \ + case n: \ + write_sysreg(val, dbg##reg##n##_el1); \ + break; + +#define ARM64_DBG_GET_CASE(reg, n, val) \ + case n: \ + val = read_sysreg(dbg##reg##n##_el1); \ + break; + +#define ARM64_DBG_SETN(reg, n, val) \ + switch (n) \ + { \ + ARM64_DBG_SET_CASE(reg, 0, val) \ + ARM64_DBG_SET_CASE(reg, 1, val) \ + ARM64_DBG_SET_CASE(reg, 2, val) \ + ARM64_DBG_SET_CASE(reg, 3, val) \ + ARM64_DBG_SET_CASE(reg, 4, val) \ + ARM64_DBG_SET_CASE(reg, 5, val) \ + ARM64_DBG_SET_CASE(reg, 6, val) \ + ARM64_DBG_SET_CASE(reg, 7, val) \ + ARM64_DBG_SET_CASE(reg, 8, val) \ + ARM64_DBG_SET_CASE(reg, 9, val) \ + ARM64_DBG_SET_CASE(reg, 10, val) \ + ARM64_DBG_SET_CASE(reg, 11, val) \ + ARM64_DBG_SET_CASE(reg, 12, val) \ + ARM64_DBG_SET_CASE(reg, 13, val) \ + ARM64_DBG_SET_CASE(reg, 14, val) \ + ARM64_DBG_SET_CASE(reg, 15, val) \ + } + +#define ARM64_DBG_GETN(reg, n) \ + ({ \ + uint64_t _val = 0; \ + switch (n) \ + { \ + ARM64_DBG_GET_CASE(reg, 0, _val) \ + ARM64_DBG_GET_CASE(reg, 1, _val) \ + ARM64_DBG_GET_CASE(reg, 2, _val) \ + ARM64_DBG_GET_CASE(reg, 3, _val) \ + ARM64_DBG_GET_CASE(reg, 4, _val) \ + ARM64_DBG_GET_CASE(reg, 5, _val) \ + ARM64_DBG_GET_CASE(reg, 6, _val) \ + ARM64_DBG_GET_CASE(reg, 7, _val) \ + ARM64_DBG_GET_CASE(reg, 8, _val) \ + ARM64_DBG_GET_CASE(reg, 9, _val) \ + ARM64_DBG_GET_CASE(reg, 10, _val) \ + ARM64_DBG_GET_CASE(reg, 11, _val) \ + ARM64_DBG_GET_CASE(reg, 12, _val) \ + ARM64_DBG_GET_CASE(reg, 13, _val) \ + ARM64_DBG_GET_CASE(reg, 14, _val) \ + ARM64_DBG_GET_CASE(reg, 15, _val) \ + } \ + _val; \ + }) + +#endif /* __ARCH_ARM64_SRC_COMMON_ARM64_HWDEBUG_H */ \ No newline at end of file diff --git a/arch/arm64/src/common/arm64_initialize.c b/arch/arm64/src/common/arm64_initialize.c index 49388ad6cb..9354e2a30c 100644 --- a/arch/arm64/src/common/arm64_initialize.c +++ b/arch/arm64/src/common/arm64_initialize.c @@ -217,5 +217,9 @@ void up_initialize(void) arm64_fpu_procfs_register(); #endif +#ifdef CONFIG_ARCH_HAVE_DEBUG + arm64_enable_dbgmonitor(); +#endif + #endif } diff --git a/arch/arm64/src/common/arm64_internal.h b/arch/arm64/src/common/arm64_internal.h index 7ac7fb09ab..4d5e38d9f6 100644 --- a/arch/arm64/src/common/arm64_internal.h +++ b/arch/arm64/src/common/arm64_internal.h @@ -393,6 +393,10 @@ size_t arm64_stack_check(void *stackbase, size_t nbytes); void arm64_stack_color(void *stackbase, size_t nbytes); #endif +#ifdef CONFIG_ARCH_HAVE_DEBUG +void arm64_enable_dbgmonitor(void); +#endif + #undef EXTERN #ifdef __cplusplus }