Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions debug/gdbserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,13 @@ def test(self):
self.access_test(8, 'long long')

class MemTestReadInvalid(SimpleMemoryTest):
def early_applicable(self):
return self.target.support_set_pmp_deny

def test(self):
bad_address = self.hart.bad_address
if self.target.support_set_pmp_deny:
self.set_pmp_deny(bad_address)
self.gdb.command("monitor riscv set_mem_access progbuf abstract")
self.set_pmp_deny(bad_address, 4)
self.gdb.command("monitor riscv set_mem_access progbuf abstract")
good_address = self.hart.ram + 0x80

self.write_nop_program(2)
Expand All @@ -296,10 +298,9 @@ def test(self):
self.gdb.stepi() # Don't let gdb cache register read
assertEqual(self.gdb.p(f"*((int*)0x{good_address:x})"), 0xabcdef)
assertEqual(self.gdb.p("$s0"), 0x12345678)
if self.target.support_set_pmp_deny:
self.reset_pmp_deny()
self.gdb.command("monitor riscv set_mem_access progbuf sysbus "
"abstract")
self.reset_pmp_deny()
self.gdb.command("monitor riscv set_mem_access progbuf sysbus "
"abstract")

#class MemTestWriteInvalid(SimpleMemoryTest):
# def test(self):
Expand Down Expand Up @@ -2200,7 +2201,7 @@ class EtriggerTest(DebugTest):
# TODO: There should be a check that a exception trigger is really not
# supported if it is marked as unsupported.
def early_applicable(self):
return self.target.support_etrigger
return self.target.support_etrigger and self.target.support_set_pmp_deny

def setup(self):
DebugTest.setup(self)
Expand All @@ -2214,9 +2215,8 @@ def test(self):
# Set fox to a bad pointer so we'll get a load access exception later.
# Use NULL if a known-bad address is not provided.
bad_address = self.hart.bad_address or 0
if self.target.support_set_pmp_deny:
self.set_pmp_deny(bad_address)
self.gdb.command("monitor riscv set_mem_access progbuf abstract")
self.set_pmp_deny(bad_address, 1)
self.gdb.command("monitor riscv set_mem_access progbuf abstract")
self.gdb.p(f"fox=(char*)0x{bad_address:08x}")
output = self.gdb.c()
# We should not be at handle_trap
Expand All @@ -2225,10 +2225,9 @@ def test(self):
# actual exception handler.
assertIn("breakpoint", output)
assertIn("trap_entry", self.gdb.where())
if self.target.support_set_pmp_deny:
self.reset_pmp_deny()
self.gdb.command("monitor riscv set_mem_access progbuf sysbus "
"abstract")
self.reset_pmp_deny()
self.gdb.command("monitor riscv set_mem_access progbuf sysbus "
"abstract")

class IcountTest(DebugTest):
compile_args = ("programs/infinite_loop.S", )
Expand Down
3 changes: 3 additions & 0 deletions debug/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class Target:
# Support set_pmp_deny to create invalid addresses.
support_set_pmp_deny = False

# Minimum PMP granularity supported by the target, in bytes.
minimum_pmp_granularity = 4

# Supports an address/data match trigger of type 2
support_mcontrol = True

Expand Down
34 changes: 32 additions & 2 deletions debug/testlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,13 @@ def classSetup(self):
# FIXME: OpenOCD doesn't handle PRIV now
#self.gdb.p("$priv=3")

def setup(self):
BaseTest.setup(self)
if self.target.support_set_pmp_deny:
assertEqual(self.target.minimum_pmp_granularity,
self.get_minimum_pmp_granularity(),
"minimum_pmp_granularity does not match target's.")

def postMortem(self):
if not self.gdb:
return
Expand Down Expand Up @@ -1489,9 +1496,32 @@ def exec_sfence_vma(self):
# PMP changes require an sfence.vma, 0x12000073 is sfence.vma
self.gdb.command("monitor riscv exec_progbuf 0x12000073")

def set_pmp_deny(self, address, size=4 * 1024):
def ctz(self, i):
# count trailing zeros
return (i & -i).bit_length() - 1

def get_minimum_pmp_granularity(self):
# Determine the minimum PMP granularity supported by this hart.
# cf. RISC-V Privileged Architecture, 3.7.1.1. Address Matching
self.gdb.p("$pmpcfg0=0") # Null region
self.gdb.p("$pmpaddr0=-1") # All ones
readback = self.gdb.p("$pmpaddr0")
return 2**(self.ctz(readback) + 2)

def set_pmp_deny(self, address, size):
# Enable physical memory protection, no permission to access specific
# address range (default 4KB).
# address range. The size must be a power of two and the address must be
# naturally aligned to the size.

# PMP requires size to be at least the minimum granularity.
# The minimum granularity for NAPOT mode is 8 bytes.
size = max(size, self.target.minimum_pmp_granularity, 8)

if 2**self.ctz(address) < size:
raise TestNotApplicable(
f"address 0x{address:x} should be naturally aligned to "
f"0x{size:x}.")

self.gdb.p("$mseccfg=0x4") # RLB
self.gdb.p("$pmpcfg0=0x98") # L, NAPOT, !R, !W, !X
self.gdb.p("$pmpaddr0="
Expand Down
Loading