From 183969629284d85da86c9efc16bc50a3537b1031 Mon Sep 17 00:00:00 2001 From: Evgeniy Naydanov Date: Fri, 6 Sep 2024 12:12:26 +0300 Subject: [PATCH] [debug] Reserve triggers propperly in HwbpManual https://github.com/riscv-collab/riscv-openocd/pull/1111 introduces a change in OpenOCD behavior: a manual trigger should be manually removed to step/resume from it. This was not concidered in previous stop-gap solutions (76ff703d9161945354e334afa45c5dfe7680da60 and 8cc4918e904ca009cd85350fadf6f44e91eca13c) This commit: 1. Determines if `reserve trigger` is supported by the target. This can be removed once https://github.com/riscv-collab/riscv-openocd/pull/1111 is merged. 2. Marks `HwbpManual` test as not applicable in case `reserve trigger` is not supported. 3. Accounts for the change in OpenOCD's behavior when stepping from a manual BP. 4. Cleans up some minor mistakes in `HwbpManual` --- debug/gdbserver.py | 80 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 1280878d3..2fd14a8e9 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -676,6 +676,47 @@ def early_applicable(self): return self.target.support_manual_hwbp and \ self.hart.instruction_hardware_breakpoint_count >= 1 + # TODO: This can be removed once + # https://github.com/riscv-collab/riscv-openocd/pull/1111 + # is merged. + def check_reserve_trigger_support(self): + not_supp_msg = "RESERVE_TRIGGER_NOT_SUPPORTED" + if not_supp_msg in self.gdb.command( + "monitor if [catch {riscv reserve_trigger 0 on} e] {echo " + + not_supp_msg + "}").splitlines(): + raise TestNotApplicable + + def set_manual_trigger(self, tdata1, tdata2): + for tselect in itertools.count(0): + self.gdb.p(f"$tselect={tselect}") + if self.gdb.p("$tselect") != tselect: + raise TestNotApplicable + + self.gdb.command( + f"monitor riscv reserve_trigger {tselect} on") + + # Need to disable the trigger before writing tdata2 + self.gdb.p("$tdata1=0") + # Need to write a valid value to tdata2 before writing tdata1 + self.gdb.p(f"$tdata2=0x{tdata2:x}") + self.gdb.p(f"$tdata1=0x{tdata1:x}") + + tdata2_rb = self.gdb.p("$tdata2") + tdata1_rb = self.gdb.p("$tdata1") + if tdata1_rb == tdata1 and tdata2_rb == tdata2: + return tselect + + type_rb = tdata1_rb & MCONTROL_TYPE(self.hart.xlen) + type_none = set_field(0, MCONTROL_TYPE(self.hart.xlen), + MCONTROL_TYPE_NONE) + if type_rb == type_none: + raise TestNotApplicable + + self.gdb.p("$tdata1=0") + self.gdb.command( + f"monitor riscv reserve_trigger {tselect} off") + assert False + def test(self): if not self.hart.honors_tdata1_hmode: # Run to main before setting the breakpoint, because startup code @@ -684,6 +725,12 @@ def test(self): self.gdb.c() self.gdb.command("delete") + + # TODO: This can be removed once + # https://github.com/riscv-collab/riscv-openocd/pull/1111 + # is merged. + self.check_reserve_trigger_support() + #self.gdb.hbreak("rot13") tdata1 = MCONTROL_DMODE(self.hart.xlen) tdata1 = set_field(tdata1, MCONTROL_TYPE(self.hart.xlen), @@ -692,26 +739,9 @@ def test(self): tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL) tdata1 |= MCONTROL_M | MCONTROL_S | MCONTROL_U | MCONTROL_EXECUTE - tselect = 0 - while True: - self.gdb.p(f"$tselect={tselect}") - value = self.gdb.p("$tselect") - if value != tselect: - raise TestNotApplicable - # Need to disable the trigger before writing tdata2 - self.gdb.p("$tdata1=0") - # Need to write a valid value to tdata2 before writing tdata1 - self.gdb.p("$tdata2=&rot13") - self.gdb.p(f"$tdata1=0x{tdata1:x}") - value = self.gdb.p("$tdata1") - if value == tdata1: - break - if value & MCONTROL_TYPE(self.hart.xlen) == MCONTROL_TYPE_NONE: - raise TestNotApplicable - self.gdb.p("$tdata1=0") - tselect += 1 + tdata2 = self.gdb.p("&rot13") - self.gdb.command(f"monitor riscv reserve_trigger {tselect} on") + tselect = self.set_manual_trigger(tdata1, tdata2) # The breakpoint should be hit exactly 2 times. for _ in range(2): @@ -728,14 +758,22 @@ def test(self): self.gdb.c() before = self.gdb.p("$pc") assertEqual(before, self.gdb.p("&crc32a")) + self.gdb.stepi() - after = self.gdb.p("$pc") - assertNotEqual(before, after) + assertEqual(before, self.gdb.p("$pc"), + "OpenOCD shouldn't disable a reserved trigger.") # Remove the manual HW breakpoint. assertEqual(tselect, self.gdb.p("$tselect")) self.gdb.p("$tdata1=0") + self.gdb.stepi() + assertNotEqual(before, self.gdb.p("$pc"), + "OpenOCD should be able to step from a removed BP.") + + self.gdb.command( + f"monitor riscv reserve_trigger {tselect} off") + self.gdb.b("_exit") self.exit()