diff options
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.py | 90 |
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} |
