summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/devices/pmc.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/devices/pmc.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/sim/devices/pmc.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/devices/pmc.py b/debug/risc-v-gsp-rm-vm/sim/devices/pmc.py
new file mode 100644
index 0000000..1ccf1cd
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/sim/devices/pmc.py
@@ -0,0 +1,90 @@
+"""PMC / master register block (first-touch device).
+
+Evidence kind: **hardware register/field header + firmware use + observed
+access pattern.**
+
+* ``NV_PMC_BOOT_42`` lives at offset ``0xA00`` of the master (PMC) block and is
+ read by the GSP boot code through the PRIV window at
+ ``0x2000_0000_0000_0A00`` (observed 4-byte read, first MMIO of the real
+ 570.144 image; the QEMU baseline faults there).
+* The firmware extracts bits ``28:20`` (``NV_PMC_BOOT_42_CHIP_ID``) and
+ compares against the TU10x IDs ``0x162..0x168``; only a match continues the
+ boot (observed branch chain at firmware vaddr ``0x4009020``).
+* ``NV_PMC_BOOT_42`` field layout: ARCHITECTURE ``28:24`` (TU100 = ``0x16``),
+ IMPLEMENTATION ``23:20``, MAJOR_REVISION ``19:16``, MINOR_REVISION
+ ``15:12``, MINOR_EXTENDED_REVISION ``11:8``, CHIP_ID ``28:20``.
+
+Only ``0xA00`` is modelled; every other offset in the window stays unhandled
+and is logged by the machine.
+"""
+from __future__ import annotations
+
+from .base import Device
+
+BOOT_42_OFFSET = 0xA00
+CHIP_ID_SHIFT = 20
+CHIP_ID_MASK = 0x1FF
+ARCHITECTURE_MASK = 0x1F
+IMPLEMENTATION_MASK = 0xF
+MAJOR_MASK = 0xF
+MINOR_MASK = 0xF
+MINOR_EXT_MASK = 0xF
+
+#: Chip IDs seen in the firmware's comparison chain (header field values).
+#: TU105 (0x165) is kept because it appears in the observed chain, but its
+#: product mapping is unverified. The former ``TU107 = 0x167`` entry was a
+#: transcription error (0x167 is TU117) and has been removed.
+TU102 = 0x162
+TU104 = 0x164
+TU105 = 0x165 # unverified product mapping (observed in the chain only)
+TU106 = 0x166
+TU116 = 0x168
+TU117 = 0x167
+
+
+def boot_42_value(chip_id: int, *, major: int = 0, minor: int = 0,
+ minor_ext: int = 0) -> int:
+ """Assemble an ``NV_PMC_BOOT_42`` value from its documented fields.
+
+ ARCHITECTURE and IMPLEMENTATION are derived from ``chip_id`` (they are the
+ upper/lower nibbles of the same bits ``28:20``), matching the header's
+ ``CHIP_ID_TU104 = 0x164`` / ``ARCHITECTURE_TU100 = 0x16`` pair.
+ """
+ chip_id &= CHIP_ID_MASK
+ arch = (chip_id >> 4) & ARCHITECTURE_MASK
+ impl = chip_id & IMPLEMENTATION_MASK
+ value = (chip_id << CHIP_ID_SHIFT)
+ value |= (arch << 24) | (impl << 20)
+ value |= (major & MAJOR_MASK) << 16
+ value |= (minor & MINOR_MASK) << 12
+ value |= (minor_ext & MINOR_EXT_MASK) << 8
+ return value
+
+
+class PmcBlock(Device):
+ """Master/PMC block: ``NV_PMC_BOOT_42`` chip identification."""
+
+ name = "pmc"
+
+ def __init__(self, *, chip_id: int = TU104, major: int = 0, minor: int = 0,
+ minor_ext: int = 0, boot_42: int | None = None,
+ base: int = 0, size: int = 0x1000):
+ super().__init__(base=base, size=size)
+ self.chip_id = chip_id
+ self.boot_42 = boot_42 if boot_42 is not None else boot_42_value(
+ chip_id, major=major, minor=minor, minor_ext=minor_ext)
+
+ def read(self, offset: int, size: int) -> int | None:
+ if offset == BOOT_42_OFFSET and size == 4:
+ return self.boot_42
+ return None
+
+ def peek(self, offset: int, size: int) -> int | None:
+ return self.read(offset, size)
+
+ def write(self, offset: int, size: int, value: int) -> bool:
+ # NV_PMC_BOOT_42 is read-only (R--4R).
+ return False
+
+ def state(self) -> dict:
+ return {"chip_id": self.chip_id, "boot_42": self.boot_42}