summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/devices/pmc.py
blob: 1ccf1cd6ebb05be3923d7e0380bbae7d8a305082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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}