From patchwork Fri May 24 09:55:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hui Li X-Patchwork-Id: 90799 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1A979385EC59 for ; Fri, 24 May 2024 09:56:00 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by sourceware.org (Postfix) with ESMTP id 1E3583858D29 for ; Fri, 24 May 2024 09:55:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1E3583858D29 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=loongson.cn ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 1E3583858D29 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=114.242.206.163 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1716544524; cv=none; b=kT4KPD7CEz/Toh6RtQ0ceLBMT5EzhC7tGYw2stAiLRTTCo2ituVjvJe33oCcGERNQWayGSFewfvC3sdDg5jNTdmvldS2Wi2wK8cH5LS2xLVr+cS5Yh6Fhp86POsL5wfye0GXDVjr44hX8H36x0x0YDR3EKLketk6jVSL9gZQF4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1716544524; c=relaxed/simple; bh=hSl6ROTqfNGkYVEYeR8OP42db12jr3K09mXZ1gVbPaY=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=LzxpYiXRU7WqAAQhH9XTCxI/PdGVHwQczwpNTtjG7HdeDqhXbDxshl/8iOdj7++PJVajRmBKA+fY8hSW7RqsBTSDBpPtBpdGTmmozjT+B4Ua0BThG3Xlv2cn0HukPFayMknwIEaXtU2tr5gjxbWKNjLS1E5LW8/vBnANgsgVAyk= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from loongson.cn (unknown [113.200.148.30]) by gateway (Coremail) with SMTP id _____8BxWeoDZFBmA1YDAA--.3672S3; Fri, 24 May 2024 17:55:15 +0800 (CST) Received: from localhost.localdomain (unknown [113.200.148.30]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxBMUAZFBmZeoHAA--.10765S3; Fri, 24 May 2024 17:55:14 +0800 (CST) From: Hui Li To: gdb-patches@sourceware.org Cc: Tiezhu Yang Subject: [PATCH 1/2] gdb: LoongArch: Add support for hardware watchpoint Date: Fri, 24 May 2024 17:55:11 +0800 Message-Id: <20240524095512.16692-2-lihui@loongson.cn> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20240524095512.16692-1-lihui@loongson.cn> References: <20240524095512.16692-1-lihui@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxBMUAZFBmZeoHAA--.10765S3 X-CM-SenderInfo: 5olk3xo6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj9fXoWDJFW8AFy5Ar4furW7Gw1rAFc_yoWrAFy3Ko WfZFW5Ww18Gr10v3yFkr1kXFyYqFs09FWUAw13Zw43Can3try5WrW0gw45J397Jrs8ur4U X348J3srJaySyF1rl-sFpf9Il3svdjkaLaAFLSUrUUUUUb8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrneAqx4xG64xvF2IEw4CE5I8CrVC2j2WlIxkvb40E47kJYxn0WfASr-VF AUDa7-sFnT9fnUUIcSsGvfJTRUUUb4xYFVCjjxCrM7AC8VAFwI0_Jr0_Gr1l1xkIjI8I6I 8E6xAIw20EY4v20xvaj40_Wr0E3s1l1IIY67AEw4v_JrI_Jryl8cAvFVAK0II2c7xJM28C jxkF64kEwVA0rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVWUJVWUCwA2z4x0Y4vE2Ix0cI 8IcVCY1x0267AKxVWUJVW8JwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E 87Iv6xkF7I0E14v26r4UJVWxJr1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_JrI_ JrylYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v2 6r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_Jrv_JF1lIxkvb4 0E47kJMIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY 6xkF7I0E14v26r1j6r4UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67 AKxVWUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v26r1j6r4UYxBIdaVFxhVjvjDU0xZFpf9x 07bwoGQUUUUU= X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_SBL_A autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org LoongArch defines hardware watchpoint functions for load/store operations. After the software configures the watchpoints for load/store, the processor hardware will monitor the access addresses of the load/store operations and trigger watchpoint exception when the watchpoint setting conditions are met. After this patch, watch/rwatch/awatch command are supported. Refer to the following document for hardware watchpoint. https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints A simple test is as follows: lihui@bogon:~$ cat test.c #include int a = 0; int main() { printf("start test\n"); a = 1; printf("a = %d\n", a); printf("end test\n"); return 0; } lihui@bogon:~$ gcc -g test.c -o test without this patch: lihui@bogon:~$ gdb test ... (gdb) start ... Temporary breakpoint 1, main () at test.c:5 5 printf("start test\n"); (gdb) awatch a Target does not support this type of hardware watchpoint. ... with this patch: lihui@bogon:~$ gdb test ... (gdb) start ... Temporary breakpoint 1, main () at test.c:5 5 printf("start test\n"); (gdb) awatch a Hardware access (read/write) watchpoint 2: a (gdb) c Continuing. start test Hardware access (read/write) watchpoint 2: a Old value = 0 New value = 1 main () at test.c:7 7 printf("a = %d\n", a); (gdb) c Continuing. Hardware access (read/write) watchpoint 2: a Value = 1 0x00000001200006e0 in main () at test.c:7 7 printf("a = %d\n", a); (gdb) c Continuing. a = 1 end test [Inferior 1 (process 22250) exited normally] Signed-off-by: Hui Li --- gdb/Makefile.in | 3 + gdb/configure.nat | 4 +- gdb/loongarch-linux-nat.c | 279 +++++++++++++++++++++++++++ gdb/loongarch-tdep.c | 1 + gdb/nat/loongarch-hw-point.c | 293 +++++++++++++++++++++++++++++ gdb/nat/loongarch-hw-point.h | 92 +++++++++ gdb/nat/loongarch-linux-hw-point.c | 227 ++++++++++++++++++++++ gdb/nat/loongarch-linux-hw-point.h | 125 ++++++++++++ gdb/nat/loongarch-linux.c | 87 +++++++++ gdb/nat/loongarch-linux.h | 42 +++++ include/elf/common.h | 2 + 11 files changed, 1154 insertions(+), 1 deletion(-) create mode 100644 gdb/nat/loongarch-hw-point.c create mode 100644 gdb/nat/loongarch-hw-point.h create mode 100644 gdb/nat/loongarch-linux-hw-point.c create mode 100644 gdb/nat/loongarch-linux-hw-point.h create mode 100644 gdb/nat/loongarch-linux.c create mode 100644 gdb/nat/loongarch-linux.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 84bc54b303e..f5864607e39 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1601,6 +1601,9 @@ HFILES_NO_SRCDIR = \ nat/linux-personality.h \ nat/linux-ptrace.h \ nat/linux-waitpid.h \ + nat/loongarch-hw-point.h \ + nat/loongarch-linux.h \ + nat/loongarch-linux-hw-point.h \ nat/mips-linux-watch.h \ nat/ppc-linux.h \ nat/x86-cpuid.h \ diff --git a/gdb/configure.nat b/gdb/configure.nat index 8b98511cef7..1bc070ef8b4 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -264,7 +264,9 @@ case ${gdb_host} in ;; loongarch) # Host: LoongArch, running GNU/Linux. - NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o linux-nat-trad.o" + NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o linux-nat-trad.o \ + nat/loongarch-hw-point.o nat/loongarch-linux.o \ + nat/loongarch-linux-hw-point.o" ;; m32r) # Host: M32R based machine running GNU/Linux diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c index 15fca6ad781..9f5760066b3 100644 --- a/gdb/loongarch-linux-nat.c +++ b/gdb/loongarch-linux-nat.c @@ -24,6 +24,9 @@ #include "linux-nat-trad.h" #include "loongarch-tdep.h" #include "nat/gdb_ptrace.h" +#include "nat/loongarch-hw-point.h" +#include "nat/loongarch-linux.h" +#include "nat/loongarch-linux-hw-point.h" #include "target-descriptions.h" #include @@ -37,6 +40,37 @@ class loongarch_linux_nat_target final : public linux_nat_trad_target void fetch_registers (struct regcache *, int) override; void store_registers (struct regcache *, int) override; + int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override; + int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) override; + + int insert_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type, + struct expression *cond) override; + int remove_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type, + struct expression *cond) override; + bool watchpoint_addr_within_range (CORE_ADDR addr, CORE_ADDR start, + int length) override; + + /* Add our hardware breakpoint and watchpoint implementation. */ + bool stopped_by_watchpoint () override; + bool stopped_data_address (CORE_ADDR *) override; + + /* Override the GNU/Linux inferior startup hook. */ + void post_startup_inferior (ptid_t) override; + + /* Override the GNU/Linux post attach hook. */ + void post_attach (int pid) override; + + /* These three defer to common nat/ code. */ + void low_new_thread (struct lwp_info *lp) override + { loongarch_linux_new_thread (lp); } + void low_delete_thread (struct arch_lwp_info *lp) override + { loongarch_linux_delete_thread (lp); } + void low_prepare_to_resume (struct lwp_info *lp) override + { loongarch_linux_prepare_to_resume (lp); } + + void low_new_fork (struct lwp_info *parent, pid_t child_pid) override; + void low_forget_process (pid_t pid) override; + protected: /* Override linux_nat_trad_target methods. */ CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regnum, @@ -406,6 +440,251 @@ fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset, sizeof (gdb_fpregset_t)); } +/* Helper for the "stopped_data_address" target method. Returns TRUE + if a hardware watchpoint trap at ADDR_TRAP matches a set watchpoint. + The address of the matched watchpoint is returned in *ADDR_P. */ + +static bool +loongarch_stopped_data_address (const struct loongarch_debug_reg_state *state, + CORE_ADDR addr_trap, CORE_ADDR *addr_p) +{ + + int i; + + for (i = loongarch_num_wp_regs - 1; i >= 0; --i) + { + const CORE_ADDR addr_watch = state->dr_addr_wp[i]; + + if (state->dr_ref_count_wp[i] + && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i]) + && addr_trap == addr_watch) + { + *addr_p = addr_watch; + return true; + } + } + return false; +} + + +/* Returns the number of hardware watchpoints of type TYPE that we can + set. Value is positive if we can set CNT watchpoints, zero if + setting watchpoints of type TYPE is not supported, and negative if + CNT is more than the maximum number of watchpoints of type TYPE + that we can support. TYPE is one of bp_hardware_watchpoint, + bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint. + CNT is the number of such watchpoints used so far (including this + one). OTHERTYPE is non-zero if other types of watchpoints are + currently enabled. */ + +int +loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, + int othertype) +{ + if (type == bp_hardware_watchpoint || type == bp_read_watchpoint + || type == bp_access_watchpoint || type == bp_watchpoint) + { + if (loongarch_num_wp_regs == 0) + return 0; + } + else if (type == bp_hardware_breakpoint) + { + return 0; + } + else + gdb_assert_not_reached ("unexpected breakpoint type"); + + /* We always return 1 here because we don't have enough information + about possible overlap of addresses that they want to watch. As an + extreme example, consider the case where all the watchpoints watch + the same address and the same region length: then we can handle a + virtually unlimited number of watchpoints, due to debug register + sharing implemented via reference counts. */ + return 1; + +} + +int +loongarch_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, + int len) +{ + return loongarch_region_ok_for_watchpoint (addr, len); +} + +/* Insert a watchpoint to watch a memory region which starts at + address ADDR and whose length is LEN bytes. Watch memory accesses + of the type TYPE. Return 0 on success, -1 on failure. */ + +int +loongarch_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + int ret; + struct loongarch_debug_reg_state *state + = loongarch_get_debug_reg_state (inferior_ptid.pid ()); + + if (show_debug_regs) + gdb_printf (gdb_stdlog, + "insert_watchpoint on entry (addr=0x%08lx, len=%d)\n", + (unsigned long) addr, len); + + gdb_assert (type != hw_execute); + + ret = loongarch_handle_watchpoint (type, addr, len, 1 /* is_insert */, + inferior_ptid, state); + + if (show_debug_regs) + { + loongarch_show_debug_reg_state (state, + "insert_watchpoint", addr, len, type); + } + + return ret; + +} + +/* Remove a watchpoint that watched the memory region which starts at + address ADDR, whose length is LEN bytes, and for accesses of the + type TYPE. Return 0 on success, -1 on failure. */ + +int +loongarch_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + int ret; + struct loongarch_debug_reg_state *state + = loongarch_get_debug_reg_state (inferior_ptid.pid ()); + + if (show_debug_regs) + gdb_printf (gdb_stdlog, + "remove_watchpoint on entry (addr=0x%08lx, len=%d)\n", + (unsigned long) addr, len); + + gdb_assert (type != hw_execute); + + ret = loongarch_handle_watchpoint (type, addr, len, 0 /* is_insert */, + inferior_ptid, state); + + if (show_debug_regs) + { + loongarch_show_debug_reg_state (state, + "remove_watchpoint", addr, len, type); + } + + return ret; + +} + +bool +loongarch_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + CORE_ADDR start, + int length) +{ + return start <= addr && start + length - 1 >= addr; +} + + +/* Implement the "stopped_data_address" target_ops method. */ + +bool +loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) +{ + siginfo_t siginfo; + struct loongarch_debug_reg_state *state; + + if (!linux_nat_get_siginfo (inferior_ptid, &siginfo)) + return false; + + /* This must be a hardware breakpoint. */ + if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != TRAP_HWBKPT) + return false; + + /* Make sure to ignore the top byte, otherwise we may not recognize a + hardware watchpoint hit. The stopped data addresses coming from the + kernel can potentially be tagged addresses. */ + struct gdbarch *gdbarch = thread_architecture (inferior_ptid); + const CORE_ADDR addr_trap + = gdbarch_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr); + + /* Check if the address matches any watched address. */ + state = loongarch_get_debug_reg_state (inferior_ptid.pid ()); + + return loongarch_stopped_data_address (state, addr_trap, addr_p); +} + +/* Implement the "stopped_by_watchpoint" target_ops method. */ + +bool +loongarch_linux_nat_target::stopped_by_watchpoint () +{ + CORE_ADDR addr; + + return stopped_data_address (&addr); +} + +/* Implement the virtual inf_ptrace_target::post_startup_inferior method. */ + +void +loongarch_linux_nat_target::post_startup_inferior (ptid_t ptid) +{ + low_forget_process (ptid.pid ()); + loongarch_linux_get_debug_reg_capacity (ptid.pid ()); + linux_nat_target::post_startup_inferior (ptid); +} + +/* Implement the "post_attach" target_ops method. */ + +void +loongarch_linux_nat_target::post_attach (int pid) +{ + low_forget_process (pid); + /* Get the hardware debug register capacity. If + loongarch_linux_get_debug_reg_capacity is not called + (as it is in loongarch_linux_child_post_startup_inferior) then + software watchpoints will be used instead of hardware + watchpoints when attaching to a target. */ + loongarch_linux_get_debug_reg_capacity (pid); + linux_nat_target::post_attach (pid); +} + +/* linux_nat_new_fork hook. */ + +void +loongarch_linux_nat_target::low_new_fork (struct lwp_info *parent, + pid_t child_pid) +{ + pid_t parent_pid; + struct loongarch_debug_reg_state *parent_state; + struct loongarch_debug_reg_state *child_state; + + /* NULL means no watchpoint has ever been set in the parent. In + that case, there's nothing to do. */ + if (parent->arch_private == NULL) + return; + + /* GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. */ + + parent_pid = parent->ptid.pid (); + parent_state = loongarch_get_debug_reg_state (parent_pid); + child_state = loongarch_get_debug_reg_state (child_pid); + *child_state = *parent_state; +} + +/* Called whenever GDB is no longer debugging process PID. It deletes + data structures that keep track of debug register state. */ + +void +loongarch_linux_nat_target::low_forget_process (pid_t pid) +{ + loongarch_remove_debug_reg_state (pid); +} + /* Initialize LoongArch Linux native support. */ void _initialize_loongarch_linux_nat (); diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index af0d6896143..757f4acc489 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -1869,6 +1869,7 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step); set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc); set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind); + set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1); /* Frame unwinders. Use DWARF debug info if available, otherwise use our own unwinder. */ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum); diff --git a/gdb/nat/loongarch-hw-point.c b/gdb/nat/loongarch-hw-point.c new file mode 100644 index 00000000000..30c08dc498c --- /dev/null +++ b/gdb/nat/loongarch-hw-point.c @@ -0,0 +1,293 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "gdbsupport/common-defs.h" +#include "gdbsupport/break-common.h" +#include "gdbsupport/common-regcache.h" +#include "loongarch-hw-point.h" +#include "loongarch-linux-hw-point.h" + +/* Number of hardware breakpoints/watchpoints the target supports. + They are initialized with values obtained via ptrace. */ + +int loongarch_num_wp_regs; + +/* Given the hardware breakpoint or watchpoint type TYPE and its + length LEN, return the expected encoding for a hardware + breakpoint/watchpoint control register. */ + +static unsigned int +loongarch_point_encode_ctrl_reg (enum target_hw_bp_type type, int len) +{ + unsigned int ctrl, ttype, llen; + + gdb_assert (len <= LOONGARCH_HWP_MAX_LEN_PER_REG); + + /* type */ + switch (type) + { + case hw_write: + ttype = 2; + break; + case hw_read: + ttype = 1; + break; + case hw_access: + ttype = 3; + break; + case hw_execute: + ttype = 0; + break; + default: + perror_with_name (_("Unrecognized watchpoint type")); + } + + /* len */ + switch (len) + { + case 1: + llen = 0b11; + break; + case 2: + llen = 0b10; + break; + case 4: + llen = 0b01; + break; + case 8: + llen = 0b00; + break; + default: + perror_with_name (_("Unrecognized watchpoint length")); + } + ctrl = 0; + if (type != hw_execute) { + /* type and length bitmask */ + ctrl |= llen << 10; + ctrl |= ttype << 8; + } + ctrl |= CTRL_PLV_ENABLE; + return ctrl; +} + + +/* Record the insertion of one breakpoint/watchpoint, as represented + by ADDR and CTRL, in the process' arch-specific data area *STATE. */ + +static int +loongarch_dr_state_insert_one_point (ptid_t ptid, + struct loongarch_debug_reg_state *state, + enum target_hw_bp_type type, CORE_ADDR addr, + int len, CORE_ADDR addr_orig) +{ + int i, idx, num_regs, is_watchpoint; + unsigned int ctrl, *dr_ctrl_p, *dr_ref_count; + CORE_ADDR *dr_addr_p; + + /* Set up state pointers. */ + is_watchpoint = (type != hw_execute); + if (is_watchpoint) + { + num_regs = loongarch_num_wp_regs; + dr_addr_p = state->dr_addr_wp; + dr_ctrl_p = state->dr_ctrl_wp; + dr_ref_count = state->dr_ref_count_wp; + } + else + { + return -1; + } + + ctrl = loongarch_point_encode_ctrl_reg (type, len); + + /* Find an existing or free register in our cache. */ + idx = -1; + for (i = 0; i < num_regs; ++i) + { + if ((dr_ctrl_p[i] & CTRL_PLV_ENABLE) == 0) // PLV0-PLV3 disable + { + gdb_assert (dr_ref_count[i] == 0); + idx = i; + /* no break; continue hunting for an exising one. */ + } + else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl) + { + idx = i; + gdb_assert (dr_ref_count[i] != 0); + break; + } + + } + + /* No space. */ + if (idx == -1) + return -1; + + /* Update our cache. */ + if ((dr_ctrl_p[idx] & CTRL_PLV_ENABLE) == 0) + { + /* new entry */ + dr_addr_p[idx] = addr; + dr_ctrl_p[idx] = ctrl; + dr_ref_count[idx] = 1; + + /* Notify the change. */ + loongarch_notify_debug_reg_change (ptid, is_watchpoint, idx); + } + else + { + /* existing entry */ + dr_ref_count[idx]++; + } + + return 0; +} + +/* Record the removal of one breakpoint/watchpoint, as represented by + ADDR and CTRL, in the process' arch-specific data area *STATE. */ + +static int +loongarch_dr_state_remove_one_point (ptid_t ptid, + struct loongarch_debug_reg_state *state, + enum target_hw_bp_type type, CORE_ADDR addr, + int len, CORE_ADDR addr_orig) +{ + int i, num_regs, is_watchpoint; + unsigned int ctrl, *dr_ctrl_p, *dr_ref_count; + CORE_ADDR *dr_addr_p; + + /* Set up state pointers. */ + is_watchpoint = (type != hw_execute); + if (is_watchpoint) + { + num_regs = loongarch_num_wp_regs; + dr_addr_p = state->dr_addr_wp; + dr_ctrl_p = state->dr_ctrl_wp; + dr_ref_count = state->dr_ref_count_wp; + } + else + { + return -1; + } + + ctrl = loongarch_point_encode_ctrl_reg (type, len); + + /* Find the entry that matches the ADDR and CTRL. */ + for (i = 0; i < num_regs; ++i) + if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl) + { + gdb_assert (dr_ref_count[i] != 0); + break; + } + + /* Not found. */ + if (i == num_regs) + return -1; + + /* Clear our cache. */ + if (--dr_ref_count[i] == 0) + { + dr_addr_p[i] = 0; + dr_ctrl_p[i] = 0; + + /* Notify the change. */ + loongarch_notify_debug_reg_change (ptid, is_watchpoint, i); + } + + return 0; +} + +int +loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr, + int len, int is_insert, ptid_t ptid, + struct loongarch_debug_reg_state *state) +{ + if (is_insert) + return loongarch_dr_state_insert_one_point (ptid, state, type, addr, + len, addr); + else + return loongarch_dr_state_remove_one_point (ptid, state, type, addr, + len, addr); +} + + +/* See nat/loongarch-hw-point.h. */ + +bool +loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state, + bool watchpoint) +{ + int count = watchpoint ? loongarch_num_wp_regs : 0; + if (count == 0) + return false; + + const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : 0; + const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : 0; + + for (int i = 0; i < count; i++) + if (addr[i] != 0 || ctrl[i] != 0) + return true; + + return false; +} + +/* Print the values of the cached breakpoint/watchpoint registers. */ + +void +loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state, + const char *func, CORE_ADDR addr, + int len, enum target_hw_bp_type type) +{ + int i; + + debug_printf ("%s", func); + if (addr || len) + debug_printf (" (addr=0x%08lx, len=%d, type=%s)", + (unsigned long) addr, len, + type == hw_write ? "hw-write-watchpoint" + : (type == hw_read ? "hw-read-watchpoint" + : (type == hw_access ? "hw-access-watchpoint" + : (type == hw_execute ? "hw-breakpoint" + : "??unknown??")))); + debug_printf (":\n"); + + debug_printf ("\tWATCHPOINTs:\n"); + for (i = 0; i < loongarch_num_wp_regs; i++) + debug_printf ("\tWP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n", + i, core_addr_to_string_nz (state->dr_addr_wp[i]), + state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]); +} + +/* Return true if we can watch a memory region that starts address + ADDR and whose length is LEN in bytes. */ + +int +loongarch_region_ok_for_watchpoint (CORE_ADDR addr, int len) +{ + /* Can not set watchpoints for zero or negative lengths. */ + if (len <= 0) + return 0; + + /* Must have hardware watchpoint debug register(s). */ + if (loongarch_num_wp_regs == 0) + return 0; + + return 1; +} diff --git a/gdb/nat/loongarch-hw-point.h b/gdb/nat/loongarch-hw-point.h new file mode 100644 index 00000000000..69965ab2165 --- /dev/null +++ b/gdb/nat/loongarch-hw-point.h @@ -0,0 +1,92 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_LOONGARCH_HW_POINT_H +#define NAT_LOONGARCH_HW_POINT_H + +/* Macro definitions, data structures, and code for the hardware + breakpoint and hardware watchpoint support follow. We use the + following abbreviations throughout the code: + + hw - hardware + bp - breakpoint + wp - watchpoint */ + +/* Maximum number of hardware breakpoint and watchpoint registers. + Neither of these values may exceed the width of dr_changed_t + measured in bits. */ + +#define LOONGARCH_HWP_MAX_NUM 8 + + +/* The maximum length of a memory region that can be watched by one + hardware watchpoint register. */ + +#define LOONGARCH_HWP_MAX_LEN_PER_REG 8 +#define CTRL_PLV_ENABLE 0x1e + +#define DR_CONTROL_ENABLED(ctrl) ((ctrl & CTRL_PLV_ENABLE) == CTRL_PLV_ENABLE) + +/* Structure for managing the hardware breakpoint/watchpoint resources. + DR_ADDR_* stores the address, DR_CTRL_* stores the control register + content, and DR_REF_COUNT_* counts the numbers of references to the + corresponding bp/wp, by which way the limited hardware resources + are not wasted on duplicated bp/wp settings (though so far gdb has + done a good job by not sending duplicated bp/wp requests). */ + +struct loongarch_debug_reg_state +{ + /* hardware watchpoint */ + CORE_ADDR dr_addr_wp[LOONGARCH_HWP_MAX_NUM]; + unsigned int dr_ctrl_wp[LOONGARCH_HWP_MAX_NUM]; + unsigned int dr_ref_count_wp[LOONGARCH_HWP_MAX_NUM]; +}; + +extern int loongarch_num_wp_regs; + +/* Invoked when IDXth breakpoint/watchpoint register pair needs to be + updated. */ + +void loongarch_notify_debug_reg_change (ptid_t ptid, int is_watchpoint, + unsigned int idx); + + +int loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr, + int len, int is_insert, ptid_t ptid, + struct loongarch_debug_reg_state *state); + +/* Return TRUE if there are any hardware breakpoints. If WATCHPOINT is TRUE, + check hardware watchpoints instead. */ + +bool loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state, + bool watchpoint); + +/* Print the values of the cached breakpoint/watchpoint registers. */ + +void loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state, + const char *func, CORE_ADDR addr, + int len, enum target_hw_bp_type type); + +/* Return true if we can watch a memory region that starts address + ADDR and whose length is LEN in bytes. */ + +int loongarch_region_ok_for_watchpoint (CORE_ADDR addr, int len); + +#endif /* NAT_LOONGARCH_HW_POINT_H */ diff --git a/gdb/nat/loongarch-linux-hw-point.c b/gdb/nat/loongarch-linux-hw-point.c new file mode 100644 index 00000000000..14ed61f0825 --- /dev/null +++ b/gdb/nat/loongarch-linux-hw-point.c @@ -0,0 +1,227 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "gdbsupport/common-defs.h" +#include "gdbsupport/break-common.h" +#include "gdbsupport/common-regcache.h" +#include "nat/linux-nat.h" +#include "loongarch-linux-hw-point.h" + +#include + +/* The order in which and are included + can be important. often declares various PTRACE_* + enums. often defines preprocessor constants for + these very same symbols. When that's the case, build errors will + result when is included before . */ + +#include +#include + +#include + +/* Hash table storing per-process data. We don't bind this to a + per-inferior registry because of targets like x86 GNU/Linux that + need to keep track of processes that aren't bound to any inferior + (e.g., fork children, checkpoints). */ + +static std::unordered_map +loongarch_debug_process_state; + +/* See loongarch-linux-hw-point.h */ + +/* Helper for loongarch_notify_debug_reg_change. Records the + information about the change of one hardware breakpoint/watchpoint + setting for the thread LWP. + N.B. The actual updating of hardware debug registers is not + carried out until the moment the thread is resumed. */ + +static int +loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint, + unsigned int idx) +{ + int tid = ptid_of_lwp (lwp).lwp (); + struct arch_lwp_info *info = lwp_arch_private_info (lwp); + dr_changed_t *dr_changed_ptr; + dr_changed_t dr_changed; + + if (!is_watchpoint) + return -1; + + if (info == NULL) + { + info = XCNEW (struct arch_lwp_info); + lwp_set_arch_private_info (lwp, info); + } + + if (show_debug_regs) + { + debug_printf ("loongarch_dr_change_callback: \n\tOn entry:\n"); + debug_printf ("\ttid%d, dr_changed_wp=0x%s\n", + tid, phex (info->dr_changed_wp, 8)); + } + + dr_changed_ptr = &info->dr_changed_wp; + dr_changed = *dr_changed_ptr; + + gdb_assert (idx >= 0 && idx <= loongarch_num_wp_regs); + + /* The actual update is done later just before resuming the lwp, + we just mark that one register pair needs updating. */ + DR_MARK_N_CHANGED (dr_changed, idx); + *dr_changed_ptr = dr_changed; + + /* If the lwp isn't stopped, force it to momentarily pause, so + we can update its debug registers. */ + if (!lwp_is_stopped (lwp)) + linux_stop_lwp (lwp); + + if (show_debug_regs) + { + debug_printf ("\tOn exit:\n\ttid%d, dr_changed_wp=0x%s\n", + tid, phex (info->dr_changed_wp, 8)); + } + + return 0; +} + +/* Notify each thread that their IDXth breakpoint/watchpoint register + pair needs to be updated. The message will be recorded in each + thread's arch-specific data area, the actual updating will be done + when the thread is resumed. */ + +void +loongarch_notify_debug_reg_change (ptid_t ptid, + int is_watchpoint, unsigned int idx) +{ + ptid_t pid_ptid = ptid_t (ptid.pid ()); + + iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info) + { + return loongarch_dr_change_callback (info, + is_watchpoint, + idx); + }); +} + +/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint + registers with data from *STATE. */ + +void +loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state, + int tid, int watchpoint) +{ + int i, count; + struct iovec iov; + struct loongarch_user_watch_state regs; + const CORE_ADDR *addr; + const unsigned int *ctrl; + + memset (®s, 0, sizeof (regs)); + iov.iov_base = ®s; + count = watchpoint ? loongarch_num_wp_regs : 0; + addr = watchpoint ? state->dr_addr_wp : 0; + ctrl = watchpoint ? state->dr_ctrl_wp : 0; + + if (count == 0) + return; + + iov.iov_len = (offsetof (struct loongarch_user_watch_state, dbg_regs) + + count * sizeof (regs.dbg_regs[0])); + for (i = 0; i < count; i++) + { + regs.dbg_regs[i].addr = addr[i]; + regs.dbg_regs[i].ctrl = ctrl[i]; + } + + if (ptrace(PTRACE_SETREGSET, tid, NT_LOONGARCH_HW_WATCH, (void *) &iov)) + { + if (errno == EINVAL) + error (_("Invalid argument setting hardware debug registers")); + else + error (_("Unexpected error setting hardware debug registers")); + } + +} + +/* Get the hardware debug register capacity information from the + process represented by TID. */ + +void +loongarch_linux_get_debug_reg_capacity (int tid) +{ + struct iovec iov; + struct loongarch_user_watch_state dreg_state; + int result; + iov.iov_base = &dreg_state; + iov.iov_len = sizeof (dreg_state); + + /* Get hardware watchpoint register info. */ + result = ptrace (PTRACE_GETREGSET, tid, NT_LOONGARCH_HW_WATCH, &iov); + + if (result == 0) + { + loongarch_num_wp_regs = LOONGARCH_DEBUG_NUM_SLOTS (dreg_state.dbg_info); + if (loongarch_num_wp_regs > LOONGARCH_HWP_MAX_NUM) + { + warning (_("Unexpected number of hardware watchpoint registers" + " reported by ptrace, got %d, expected %d."), + loongarch_num_wp_regs, LOONGARCH_HWP_MAX_NUM); + loongarch_num_wp_regs = LOONGARCH_HWP_MAX_NUM; + } + } + else + { + warning (_("Unable to determine the number of hardware watchpoints" + " available.")); + loongarch_num_wp_regs = 0; + } + +} + +/* Return the debug register state for process PID. If no existing + state is found for this process, return nullptr. */ + +struct loongarch_debug_reg_state * +loongarch_lookup_debug_reg_state (pid_t pid) +{ + auto it = loongarch_debug_process_state.find (pid); + if (it != loongarch_debug_process_state.end ()) + return &it->second; + + return nullptr; +} + +/* Return the debug register state for process PID. If no existing + state is found for this process, create new state. */ + +struct loongarch_debug_reg_state * +loongarch_get_debug_reg_state (pid_t pid) +{ + return &loongarch_debug_process_state[pid]; +} + +/* Remove any existing per-process debug state for process PID. */ + +void +loongarch_remove_debug_reg_state (pid_t pid) +{ + loongarch_debug_process_state.erase (pid); +} diff --git a/gdb/nat/loongarch-linux-hw-point.h b/gdb/nat/loongarch-linux-hw-point.h new file mode 100644 index 00000000000..4086907e3c0 --- /dev/null +++ b/gdb/nat/loongarch-linux-hw-point.h @@ -0,0 +1,125 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_LOONGARCH_LINUX_HW_POINT_H +#define NAT_LOONGARCH_LINUX_HW_POINT_H + +#include "gdbsupport/break-common.h" /* For enum target_hw_bp_type. */ + +#include "nat/loongarch-hw-point.h" + +struct loongarch_user_watch_state { + uint64_t dbg_info; + struct { + uint64_t addr; + uint64_t mask; + uint32_t ctrl; + uint32_t pad; + } dbg_regs[8]; +}; + + +/* Macros to extract fields from the hardware debug information word. */ +#define LOONGARCH_DEBUG_NUM_SLOTS(x) ((x) & 0xffff) + +/* Each bit of a variable of this type is used to indicate whether a + hardware breakpoint or watchpoint setting has been changed since + the last update. + + Bit N corresponds to the Nth hardware breakpoint or watchpoint + setting which is managed in loongarch_debug_reg_state, where N is + valid between 0 and the total number of the hardware breakpoint or + watchpoint debug registers minus 1. + + When bit N is 1, the corresponding breakpoint or watchpoint setting + has changed, and therefore the corresponding hardware debug + register needs to be updated via the ptrace interface. + + In the per-thread arch-specific data area, we define two such + variables for per-thread hardware breakpoint and watchpoint + settings respectively. + + This type is part of the mechanism which helps reduce the number of + ptrace calls to the kernel, i.e. avoid asking the kernel to write + to the debug registers with unchanged values. */ + +typedef ULONGEST dr_changed_t; + +/* Set each of the lower M bits of X to 1; assert X is wide enough. */ + +#define DR_MARK_ALL_CHANGED(x, m) \ + do \ + { \ + gdb_assert (sizeof ((x)) * 8 >= (m)); \ + (x) = (((dr_changed_t)1 << (m)) - 1); \ + } while (0) + +#define DR_MARK_N_CHANGED(x, n) \ + do \ + { \ + (x) |= ((dr_changed_t)1 << (n)); \ + } while (0) + +#define DR_CLEAR_CHANGED(x) \ + do \ + { \ + (x) = 0; \ + } while (0) + +#define DR_HAS_CHANGED(x) ((x) != 0) +#define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n))) + +/* Per-thread arch-specific data we want to keep. */ + +struct arch_lwp_info +{ + /* When bit N is 1, it indicates the Nth hardware breakpoint or + watchpoint register pair needs to be updated when the thread is + resumed; see loongarch_linux_prepare_to_resume. */ + dr_changed_t dr_changed_wp; +}; + +/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint + registers with data from *STATE. */ + +void loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state, + int tid, int watchpoint); + +/* Get the hardware debug register capacity information from the + process represented by TID. */ + +void loongarch_linux_get_debug_reg_capacity (int tid); + +/* Return the debug register state for process PID. If no existing + state is found for this process, return nullptr. */ + +struct loongarch_debug_reg_state *loongarch_lookup_debug_reg_state (pid_t pid); + +/* Return the debug register state for process PID. If no existing + state is found for this process, create new state. */ + +struct loongarch_debug_reg_state *loongarch_get_debug_reg_state (pid_t pid); + +/* Remove any existing per-process debug state for process PID. */ + +void loongarch_remove_debug_reg_state (pid_t pid); + + +#endif /* NAT_LOONGARCH_LINUX_HW_POINT_H */ diff --git a/gdb/nat/loongarch-linux.c b/gdb/nat/loongarch-linux.c new file mode 100644 index 00000000000..088d0fcbf34 --- /dev/null +++ b/gdb/nat/loongarch-linux.c @@ -0,0 +1,87 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "gdbsupport/common-defs.h" +#include "gdbsupport/break-common.h" +#include "nat/linux-nat.h" +#include "nat/loongarch-linux-hw-point.h" +#include "nat/loongarch-linux.h" + +#include "elf/common.h" +#include "nat/gdb_ptrace.h" +#include +#include + +/* Called when resuming a thread LWP. + The hardware debug registers are updated when there is any change. */ + +void +loongarch_linux_prepare_to_resume (struct lwp_info *lwp) +{ + struct arch_lwp_info *info = lwp_arch_private_info (lwp); + + /* NULL means this is the main thread still going through the shell, + or, no watchpoint has been set yet. In that case, there's + nothing to do. */ + if (info == NULL) + return; + + if (DR_HAS_CHANGED (info->dr_changed_wp)) + { + ptid_t ptid = ptid_of_lwp (lwp); + int tid = ptid.lwp (); + struct loongarch_debug_reg_state *state + = loongarch_get_debug_reg_state (ptid.pid ()); + + if (show_debug_regs) + debug_printf ("prepare_to_resume thread %d\n", tid); + + loongarch_linux_set_debug_regs (state, tid, 1); + DR_CLEAR_CHANGED (info->dr_changed_wp); + + } +} + +/* Function to call when a new thread is detected. */ + +void +loongarch_linux_new_thread (struct lwp_info *lwp) +{ + ptid_t ptid = ptid_of_lwp (lwp); + struct loongarch_debug_reg_state *state + = loongarch_get_debug_reg_state (ptid.pid ()); + struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); + + /* If there are hardware breakpoints/watchpoints in the process then mark that + all the hardware breakpoint/watchpoint register pairs for this thread need + to be initialized (with data from arch_process_info.debug_reg_state). */ + if (loongarch_any_set_debug_regs_state (state, true)) + DR_MARK_ALL_CHANGED (info->dr_changed_wp, loongarch_num_wp_regs); + + lwp_set_arch_private_info (lwp, info); +} + +/* See nat/loongarch-linux.h. */ + +void +loongarch_linux_delete_thread (struct arch_lwp_info *arch_lwp) +{ + xfree (arch_lwp); +} diff --git a/gdb/nat/loongarch-linux.h b/gdb/nat/loongarch-linux.h new file mode 100644 index 00000000000..f4bb75af88c --- /dev/null +++ b/gdb/nat/loongarch-linux.h @@ -0,0 +1,42 @@ +/* Native-dependent code for GNU/Linux on LoongArch processors. + + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_LOONGARCH_LINUX_H +#define NAT_LOONGARCH_LINUX_H + +#include + +/* Defines ps_err_e, struct ps_prochandle. */ +#include "gdb_proc_service.h" + +/* Called when resuming a thread LWP. + The hardware debug registers are updated when there is any change. */ + +void loongarch_linux_prepare_to_resume (struct lwp_info *lwp); + +/* Function to call when a new thread is detected. */ + +void loongarch_linux_new_thread (struct lwp_info *lwp); + +/* Function to call when a thread is being deleted. */ + +void loongarch_linux_delete_thread (struct arch_lwp_info *arch_lwp); + +#endif /* LOONGARCH_LINUX_H */ diff --git a/include/elf/common.h b/include/elf/common.h index 7e62c34643c..f993146717c 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -720,6 +720,8 @@ /* note name must be "LINUX". */ #define NT_LARCH_LBT 0xa04 /* LoongArch Binary Translation registers */ /* note name must be "CORE". */ +#define NT_LOONGARCH_HW_WATCH 0xa06 /* LoongArch hardware watchpoint registers */ + /* note name must be "LINUX". */ #define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ /* note name must be "LINUX". */ #define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */