summaryrefslogtreecommitdiffstats
path: root/wiki/Introduction-to-GMMU.md
blob: f5cd8d5b31141ffb0b03ea1f8f4e5c64c4f477b9 (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# Introduction to the GMMU

> **Scope.** A from-first-principles introduction to the GPU Memory Management
> Unit (GMMU): what it translates, how its page tables work on our Turing
> reference, how the host module uses them, and how the GMMU relates to the CPU
> MMU and the platform IOMMU/SMMU. The architecture chapter is
> [`../docs/03-architecture/05-gmmu-amap-hal-gr.md`](../docs/03-architecture/05-gmmu-amap-hal-gr.md);
> the isolation policy authority is
> [`../docs/03-architecture/14-multi-tenant-gmmu-protection.md`](../docs/03-architecture/14-multi-tenant-gmmu-protection.md)
> (condensed in [`GMMU-Multi-Tenant-Protection.md`](GMMU-Multi-Tenant-Protection.md)).
>
> **Evidence discipline.** Hardware-format claims condense the tracked field
> catalogue
> [`../debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md`](../debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md)
> and live observations; numeric IDs, layouts and code anchors are cited, and no
> timing/cycle claim is made.
>
> Status: 2026-10-11, tree `master @ 08f39c2f`. Tags: `[live]` observed with the
> stock guest driver; `[proven]` hardware-format fact; `[hypothesis]`
> unexercised; `[design]` not built.

## 1. What the GMMU is

The **GMMU (GPU Memory Management Unit)** is the address-translation hardware
inside the GPU. Every engine — GR (shaders and CUDA kernels), CE (copy engines),
SEC2, the video engines — addresses memory through **GPU virtual addresses
(VAs)**, and the GMMU translates each VA into a physical address: an offset in
GPU local memory (**vidmem**/VRAM), an address in system memory (**sysmem**), or
a peer GPU's memory across a fabric.

It is a paged MMU, conceptually the CPU MMU's sibling:

* **Multi-level page tables** describe the mappings; the root is a **page
  directory base (PDB)**.
* A **TLB** caches translations so the walk is not paid per access.
* Translation failures raise **faults** (invalid/missing leaf, missing table),
  reported through engine fault buffers.
* **Each context has its own page directory (VA space)**, so one context cannot
  name another context's mappings — this is the hardware isolation boundary the
  project builds multi-tenancy on.

What makes it *not* just "the CPU MMU on a GPU":

* The tables live in GPU-visible memory (vidmem or sysmem), and each directory
  entry says which aperture the next level lives in.
* Entries carry GPU-specific **aperture** (which physical space) and
  **memory-kind** fields that the CPU MMU does not have.
* The "physical" addresses inside the tables are **guest-physical addresses or
  emulated-VRAM offsets** in our virtualized setting; the host module owns the
  resolution to real host addresses (§4.2). On a bare-metal GPU the same
  structures point at real vidmem/sysmem.

For this project the GMMU matters three ways: the guest driver programs it
exactly as on bare metal (no guest patches); the guest's page tables are the
ground truth for *where its memory actually is*; and the per-context page
directory is the only proven hardware isolation primitive available on the
reference generation (§4.2).

## 2. The three MMUs: CPU MMU, GMMU, IOMMU/SMMU

| | CPU MMU | GMMU | IOMMU / SMMU |
|---|---|---|---|
| Translates | process VA → host physical address | engine GPU VA → GPU physical (vidmem / sysmem / peer) | device DMA address → host physical address |
| Lives in | the CPU core | the GPU | the host platform (IOMMU: Intel VT-d / AMD-Vi) or the SoC (SMMU, ARM) |
| Programmed by | the OS kernel (per-process tables) | the GPU driver / RM (the *guest* driver here) | the hypervisor / OS (per-device or per-VM domains) |
| Table unit | per-process page tables (e.g. CR3) | per-context page directory (VA space) | per-device/per-VM translation domain |
| Isolation scope | process ↔ process | context ↔ context (engine-level) | VM/device ↔ VM/device (platform DMA) |
| In our stack | guest and host kernels | guest driver builds the tables; host walks or plants its own | hypervisor enforces it; the module resolves GPAs itself when it needs host addresses |

They are three instances of the same idea — *page tables plus a TLB give virtual
memory and isolation to an agent* — at three different points in the address
path, owned by three different software layers. In a virtualized guest a GPU
access passes through **two** of them:

```text
engine (GR / CE)
   │  GPU VA
   ▼
GMMU  (inside the GPU, driven by the guest's page tables)
   │  guest physical address (GPA) / GPU local address
   ▼
IOMMU / SMMU  (in the host platform, programmed by the hypervisor for this VM)
   │  host physical address (HPA)
   ▼
host DRAM / real VRAM
```

The two stages are independent: the **GMMU** decides *which guest page* an
engine touches; the **IOMMU/SMMU** decides *whether this device may reach that
page at all* and where it lives in host memory. GMMU isolation between guest
contexts is the GPU driver/RM's job; IOMMU isolation between VMs is the
platform's job.

**How the two meet in this project:**

* In real vGPU, the host vGPU manager uses the platform IOMMU to establish the
  GPU's view of guest memory. Here the host module does the equivalent
  resolution itself.
* On the reference host the physical GPU's IOMMU group is an **identity domain**
  (all-RAM identity) and the mediated device registers as an **emulated-IOMMU
  VFIO device**: its type-1 container holds guest DMA mappings privately and no
  kernel `iommu_domain` exists for it, so `iommu_iova_to_phys` cannot translate
  guest GPAs. Guest RAM is backed by a QEMU shared file and the module
  translates GPA→HPA through that file's page cache, behind the active-mdev gate
  (a per-mdev-gated translation, *not* itself an IOMMU guarantee).
* The project rule is explicit: **memory isolation is the platform IOMMU's job,
  and we never weaken it** — DMA only through the hypervisor-provided interface
  for the device's own VM, no global mappings, fail closed if a required
  isolation property cannot be established.
* On aarch64 hosts the **SMMU** plays the IOMMU's role; page sizes, cache
  attributes and SMMU behaviour are on the host-portability list for the
  Grace/GB10 targets.

## 3. How the GMMU works (Turing reference)

### 3.1 Page-tree geometry

The Turing page-tree path inherits the Pascal/Volta geometry and uses the
**VER2** entry encoding. Depths run root (0) to leaf (4); VA width is **49
bits**:

| Depth | HW level | VA bits | Index bits | Entry size | Role |
|---|---|---|---|---|---|
| 0 | PDE3 | 48:47 | 2 | 8 B | pointer PDE |
| 1 | PDE2 | 46:38 | 9 | 8 B | pointer PDE |
| 2 | PDE1 | 37:29 | 9 | 8 B | pointer PDE (`NO_ATS` set here) |
| 3 | PDE0 | 28:21 | 8 | 16 B (dual) / 8 B (2 MB leaf) | dual 64K/4K PDE, or a 2 MB leaf PTE |
| 4 | PTE | 20:12 (4 KB) / 20:16 (64 KB) | 9 / 5 | 8 B | leaf PTE |

Supported page sizes are **2 MB** (depth 3), **64 KB** and **4 KB** (depth 4).
A leaf entry alone does not say 4 KB vs 64 KB — the table's depth and page-size
context do — so decoders must report `4k_or_64k` rather than guess.

### 3.2 Entry encoding (VER2)

**Leaf PTE (8 bytes):**

| Field | Bits | Meaning |
|---|---|---|
| `valid` | 0 | entry is present |
| `aperture` | 2:1 | 0 video, 1 peer, 2 system coherent, 3 system non-coherent |
| `volatile` | 3 | cache/volatility bit |
| `encrypted` | 4 | encrypted memory |
| `privilege` | 5 | privileged access |
| `read_only` | 6 | write protection |
| `atomic_disable` | 7 | atomics disabled |
| `address_sys` | 53:8 | system-aperture address bits (PA 53:12) |
| `address_vid` | 32:8 | video/peer address bits (PA 36:12) |
| `peer_id` | 35:33 | peer/fabric id (peer aperture) |
| `comptagline` | 55:36 | compression tag (overloaded as high address bits for the 47-bit peer case) |
| `kind` | 63:56 | memory kind id |

**Directory PDE (8 bytes)** is the same shape with one discriminator change:
bit 0 is `is_pde_pointer` (0 = pointer to a lower table, 1 = a 2 MB leaf PTE),
and **aperture 0 means invalid at directory level** (it means video memory at
leaf level). Depth 2 sets `NO_ATS` (bit 5).

**Dual PDE0 (16 bytes)** holds two halves for mixed 64K/4K tables: BIG (low 64
bits) and SMALL (high 64 bits); bit 0 discriminates the whole slot.

Live guest note: the client's leaf entries in the format we observe are
`PA[47:12] | flags`, with `flags = 0xe` for a valid system-memory leaf. The
`kind` field carries numeric memory-kind ids (243 named values); common compute
values are `0x00 PITCH`, `0x06 GENERIC_MEMORY`, `0x07 INVALID`,
`0x0F SMSKED_MESSAGE`, `0xFE GENERIC_16BX2`.

The decoder (`sim/gmmu_pte.py`, docs/43) is decode-only: unsupported encodings
fail closed, set bits not covered by a named field are reported as reserved, and
nothing is guessed.

### 3.3 Walking, caching and faults

A walk starts at the channel's **page-directory base** and follows
`PDE3 → PDE2 → PDE1 → PDE0 → PTE`; the leaf yields the physical address, page
size, aperture and access permissions. The TLB caches results, and there is a
second, coarser cache that matters operationally:

* **PTE fetch-line rule (learned live):** the engine caches PTE fetches at
  **128-byte (16-PTE) granularity**, and the forwarding code has **no
  invalidate path**. A runtime PTE write into a line the GMMU has already walked
  is invisible to the engine. Consequences: page-table slots for a guest window
  are **pre-built before any engine access**, and each forwarded copy pair
  consumes a whole fresh line from a pre-zeroed pool (16 page-table pages,
  capacity 512 pairs per boot in the current design) — a line is never reused
  within a boot.
* **Faults:** invalid or missing leaves and missing tables raise GMMU faults;
  the driver allocates fault buffers whose sizes come from
  `INTERNAL_GMMU_GET_STATIC_INFO` (control `0x20800a59`): **64 KB replayable /
  4 KB legacy** — the values the emulation serves, because a wrong value breaks
  UVM early.

## 4. How the GMMU is used

### 4.1 Guest side (the stock driver)

* The guest driver allocates VA spaces and **builds the page tables**, then
  publishes the directory per channel/context through `SET_PAGE_DIRECTORY`
  (`hVASpace` + `chId`; the host records the directory per owner in
  `kernel/gsp_rm_emulation.c:3307-3319`).
* Memory descriptors carry an aperture: `ADDR_SYSMEM = 1`, `ADDR_FBMEM = 2`.
* **Server-owned ranges:** `COPY_SERVER_RESERVED_PDES` (control `0x90f10106`)
  tells the client that some page-table ranges are populated by the "server"
  (us). The exact division of labour for `RESERVE_ENTRIES`/`RELEASE_ENTRIES`
  and invalidations is a tracked known-unknown.
* Channels submit push buffers that reference GPU VAs; the engines walk the
  GMMU to reach the data.

### 4.2 Host side (`vgsp.ko`)

* **Truth.** The host resolves guest VAs by **walking the driver-published page
  directories itself** (the trusted GMMU walk in `kernel/gsp_sema_service.c`,
  VER2 geometry at `:1058-1072`, decode `:1093`, walk `:1174`), plus injected
  pins and content-keyed rescue when neither names a mapping. This is what lets
  the software path apply a semaphore release to the correct host page.
* **Forwarding.** For real CE copies the module builds its **own scratch page
  tables** that map already-resolved host pages (guest RAM through the shared
  file; shadow pages through the emulated-VRAM mapping), under the fetch-line
  rules of §3.3. Real guest page tables are *not yet* programmed into the real
  MMU — guest submission on hardware is the end state (P6).
* **Emulation.** Guest vidmem is a private host-RAM buffer per mdev
  (`kernel/bar_init.c:68-77`); FB-aperture accesses stay inside the owning
  mdev's buffer, and guest RAM is reached only through the hypervisor's
  per-mdev DMA interface (`vfio_dma_rw`).
* **Isolation.** The per-context page directory is the **only proven hardware
  isolation primitive** on this generation: the VER2 PTE/PDE has **no
  tenant/ACL/PASID field** (proven absence in the named field set; `peer_id` is
  a fabric peer selector, not a security domain). `read_only` (bit 6) is a
  proven *format* field, but its *enforcement* is a `[hypothesis]` until the M7
  supervised probe. The multi-tenant design therefore places decisions in
  **topology** (which directory a page appears in, which channel may name it)
  plus software validation and audit in the execution plane — see
  [`GMMU-Multi-Tenant-Protection.md`](GMMU-Multi-Tenant-Protection.md).
* **Versioning.** GMMU controls, static info and entry formats are
  version/hardware-keyed; formats are expected to differ per generation and must
  be re-derived by measurement, never guessed.

### 4.3 Tooling built around it

| Tool / doc | Question it answers |
|---|---|
| PTE decoder `sim/gmmu_pte.py` + [docs/43](../debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md) | decode a captured entry field-by-field (decode-only, fail closed) |
| Walker `sim/gmmu_walk.py` + [docs/48](../debug/risc-v-gsp-rm-vm/docs/48-GMMU-WALK-AND-LAUNCH-AUDIT.md) | walk a captured PDB to the leaf mapping or the exact fault (4K/2M validated against a live captured channel PDB; 64K modelled) |
| Launch audit `harness/launch_audit.py` + docs/48 | where the engine would read/write each leg of a submission: `ok` / `pa_mismatch` / `unmapped` / `access_denied` |
| Oracle workflow [docs/44](../debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md) | compare simulated vs observed GMMU/FBIF register values, per register, with fidelity labels |
| Observe-only windows [docs/42](../debug/risc-v-gsp-rm-vm/docs/42-GMMU-FBIF-OBSERVABILITY.md) | record what firmware touches in the GMMU register windows without faking semantics |

## 5. Limits and open questions

* **`read_only` enforcement** is unproven (M7 supervised probe); **encryption
  (CCS) semantics** are unstudied. Neither can be assumed as a tenant boundary.
* **Invalidation semantics** are not fully modelled: the 128-byte fetch-line
  observation is live evidence, not a complete invalidate contract.
* The **64K dual path** is modelled from the format definitions, not yet
  validated against a capture.
* **Newer generations** (Ampere/Hopper/Blackwell, GB10) are expected to change
  the entry format; each must be re-derived. RM 610 work sits behind its own
  human approval gate.
* **No timing/cycle claims** are made anywhere here; zero hits in a run are a
  coverage fact, not proof of dead code.
* Multi-mdev isolation is first a **service-state-ownership** problem (host
  tables keyed by guest-chosen handles/VAs alone collide); GMMU policy is built
  on top of that fix, not instead of it.

## 6. Related documentation

* [`../docs/03-architecture/05-gmmu-amap-hal-gr.md`](../docs/03-architecture/05-gmmu-amap-hal-gr.md) — architecture chapter: GMMU, AMAPlib/HALs, channels, runlists, GR.
* [`GMMU-Multi-Tenant-Protection.md`](GMMU-Multi-Tenant-Protection.md) — the isolation design condensed.
* [`../docs/03-architecture/14-multi-tenant-gmmu-protection.md`](../docs/03-architecture/14-multi-tenant-gmmu-protection.md) — canonical multi-tenant GMMU policy.
* [`Execution-Plane.md`](Execution-Plane.md) — how guest work becomes physical execution; the fetch-line rule in context.
* [`../docs/03-architecture/11-physical-backend-guest-wiring.md`](../docs/03-architecture/11-physical-backend-guest-wiring.md) — page-table seams and the guest-memory reachability model.
* [`../docs/04-implementation/01-known-knowns-and-unknowns.md`](../docs/04-implementation/01-known-knowns-and-unknowns.md) — server-owned page tables and the IOMMU reachability note.
* [`../docs/05-operations/01-performance-correctness-security.md`](../docs/05-operations/01-performance-correctness-security.md) — threat model and the IOMMU rules.
* [`../debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md`](../debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md), [docs/44](../debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md), [docs/48](../debug/risc-v-gsp-rm-vm/docs/48-GMMU-WALK-AND-LAUNCH-AUDIT.md) — the field catalogue and the offline walk/audit tooling.