summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/machine.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/machine.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/sim/machine.py80
1 files changed, 68 insertions, 12 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/machine.py b/debug/risc-v-gsp-rm-vm/sim/machine.py
index 9908753..7634ce1 100644
--- a/debug/risc-v-gsp-rm-vm/sim/machine.py
+++ b/debug/risc-v-gsp-rm-vm/sim/machine.py
@@ -19,6 +19,7 @@ import mmap
import struct
from .cycles import DEFAULT_MODEL
+from .mpu import MSTATUS, WATCHED_CSRS, RiscvMpu
from .decode import (
A_ADD,
A_AND,
@@ -84,6 +85,9 @@ R_ECALL = 6
#: Accepted ``unknown_mmio`` policies for accesses outside RAM/UART/the map.
UNKNOWN_MMIO_MODES = ("fault", "lenient")
+#: CSRs whose writes update the MMU model (M-mode protection unit + mstatus).
+_MPU_CSRS = frozenset(WATCHED_CSRS) | {MSTATUS}
+
UART_BASE = 0x10000000
UART_SIZE = 0x100
@@ -232,6 +236,10 @@ class Machine:
self.device_map = device_map
self.devices = devices
self.translator = translator # aperture -> RAM seam (sim.devices.dma)
+ self.mpu = RiscvMpu() # M-mode protection unit (sim.mpu)
+ #: Optional guest-ECALL service (``handle(machine) -> (handled, ret)``);
+ #: when absent (or unhandled) ``run`` stops with ``R_ECALL`` as before.
+ self.syscall_handler = None
self.unknown_mmio = unknown_mmio
self.learn_mmio = bool(learn_mmio)
self.bias = bias # PC relocation for learned-map firmware addresses
@@ -506,6 +514,10 @@ class Machine:
a = b = 0
traced = trace is not None
active = False
+ # M-mode protection unit (sim.mpu): translation is consulted only when
+ # an enabled entry exists; no match is treated as identity.
+ mpu = self.mpu
+ mpu_on = mpu.active
# Modeled cycle accounting (sim.cycles): pure table lookups, no timing.
cycle_base = self._cycle_base
cycle_op = self._cycle_op
@@ -520,7 +532,12 @@ class Machine:
a = pc
break
- off = pc - base
+ fetch_pc = pc
+ if mpu_on:
+ translated = mpu.translate(pc)
+ if translated is not None:
+ fetch_pc = translated
+ off = fetch_pc - base
if off < 0 or off + 4 > limit:
reason = R_FAULT
a = 1
@@ -558,6 +575,12 @@ class Machine:
b = hit
break
o = ea - base
+ mem_ea = ea
+ if mpu_on:
+ translated = mpu.translate(ea)
+ if translated is not None:
+ mem_ea = translated
+ o = mem_ea - base
if 0 <= o <= limit - size:
if active:
trace.mem(insn_pc, ea, size, False, self.insns + n)
@@ -587,7 +610,7 @@ class Machine:
pc = nxt
else:
try:
- v = self._mmio_load(ea, size, pc)
+ v = self._mmio_load(mem_ea, size, pc)
except Fault as f:
reason = R_FAULT
a = f.cause
@@ -613,6 +636,12 @@ class Machine:
b = hit
break
o = ea - base
+ mem_ea = ea
+ if mpu_on:
+ translated = mpu.translate(ea)
+ if translated is not None:
+ mem_ea = translated
+ o = mem_ea - base
value = x[r2]
if 0 <= o <= limit - size:
if active:
@@ -627,17 +656,20 @@ class Machine:
ram[o] = value & 0xFF
# Self-modifying code: drop any cached decode overlapping
# the written range (firmware patches code at runtime).
- p = ea & ~1
- end = ea + size
- while p < end:
- cache.pop(p, None)
- p += 2
+ # Both the virtual address and its translated backing are
+ # dropped so a fetch through either mapping re-decodes.
+ for target in ((ea, mem_ea) if mem_ea != ea else (ea,)):
+ p = target & ~1
+ end = target + size
+ while p < end:
+ cache.pop(p, None)
+ p += 2
if counting:
cycle_sum += cycle_mem
pc = nxt
else:
try:
- self._mmio_store(ea, size, value, pc)
+ self._mmio_store(mem_ea, size, value, pc)
except Fault as f:
reason = R_FAULT
a = f.cause
@@ -864,6 +896,9 @@ class Machine:
a = 2
b = csr
break
+ if csr in _MPU_CSRS:
+ mpu.on_csr_write(csr, self.csrs.get(csr, 0))
+ mpu_on = mpu.active
pc = nxt
elif kind == K_AMO:
@@ -938,9 +973,20 @@ class Machine:
elif kind == K_ECALL:
if single_step:
pc = nxt
+ elif self.syscall_handler is not None:
+ handled, result = self.syscall_handler(self, pc)
+ if not handled:
+ reason = R_ECALL
+ a = pc
+ b = x[5] # syscall number (t0), for diagnostics
+ break
+ if result is not None:
+ x[10] = result & MASK64
+ pc = nxt
else:
reason = R_ECALL
a = pc
+ b = x[5]
break
elif kind == K_MRET:
@@ -975,15 +1021,25 @@ class Machine:
# -- guest memory helpers (AMO) ----------------------------------------
def _guest_load(self, addr: int, width: int, pc: int = 0) -> int:
- off = addr - self.ram_base
+ mem = addr
+ if self.mpu.active:
+ translated = self.mpu.translate(addr)
+ if translated is not None:
+ mem = translated
+ off = mem - self.ram_base
if 0 <= off <= self.ram_size - width:
if width == 8:
return self._u64.unpack_from(self.ram, off)[0]
return self._u32.unpack_from(self.ram, off)[0]
- return self._mmio_load(addr, width, pc)
+ return self._mmio_load(mem, width, pc)
def _guest_store(self, addr: int, width: int, value: int, pc: int = 0) -> None:
- off = addr - self.ram_base
+ mem = addr
+ if self.mpu.active:
+ translated = self.mpu.translate(addr)
+ if translated is not None:
+ mem = translated
+ off = mem - self.ram_base
if 0 <= off <= self.ram_size - width:
if width == 8:
self._u64p(self.ram, off, value)
@@ -995,7 +1051,7 @@ class Machine:
self._cache.pop(p, None)
p += 2
return
- self._mmio_store(addr, width, value, pc)
+ self._mmio_store(mem, width, value, pc)
#: Task-facing alias: the native simulator machine is :class:`Machine`.