summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/debug/pdb_scan.c234
-rw-r--r--tools/debug/pdb_scan_gmmu.c330
2 files changed, 564 insertions, 0 deletions
diff --git a/tools/debug/pdb_scan.c b/tools/debug/pdb_scan.c
new file mode 100644
index 0000000..da7227e
--- /dev/null
+++ b/tools/debug/pdb_scan.c
@@ -0,0 +1,234 @@
+/*
+ * pdb_scan.c - read-only reverse scan of a QEMU pc.ram backing file for the
+ * 4-level page-table chain that maps a given VA to a given PA.
+ *
+ * Build:
+ * cc -O2 -Wall -Wextra -o pdb_scan pdb_scan.c
+ *
+ * Usage:
+ * sudo ./pdb_scan /dev/shm/vgpu-guest-ram 0xafef8000 0x4100000
+ *
+ * Arguments: <ramfile> <target_pa> [va]
+ * - target_pa : physical address the leaf PT entry must point at (page aligned)
+ * - va : optional VA used to derive the four table indices
+ * (default 0x4100000: PML4[0], PDPT[0], PD[8], PT[0x100])
+ *
+ * The scan is strictly read-only: the file is mmap()ed PROT_READ/MAP_PRIVATE
+ * and no writes are issued. It builds two reverse indexes:
+ * entry[0x000] -> pages (used for PML4[0] and PDPT[0])
+ * entry[0x040] -> pages (used for PD[8])
+ * then joins them against pages whose entry[0x800] points at target_pa.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define PAGE_SHIFT 12ULL
+#define PAGE_SIZE (1ULL << PAGE_SHIFT)
+#define PA_MASK 0x000FFFFFFFFFF000ULL
+
+static uint64_t target_pa;
+static uint64_t va;
+static uint64_t n_pages;
+static const uint8_t *ram;
+
+static unsigned idx_pml4, idx_pdpt, idx_pd, idx_pt;
+static const char *idx_names[4] = {"PML4", "PDPT", "PD", "PT"};
+
+static inline uint64_t rd64(uint64_t pa)
+{
+ uint64_t v;
+ memcpy(&v, ram + pa, sizeof(v));
+ return v;
+}
+
+static inline uint64_t entry_mask(uint64_t e) { return e & PA_MASK; }
+
+static const char *bits(uint64_t e)
+{
+ static char b[128];
+ snprintf(b, sizeof(b), "P=%d RW=%d US=%d PWT=%d PCD=%d A=%d D=%d PS=%d",
+ (int)(e & 1), (int)((e >> 1) & 1), (int)((e >> 2) & 1),
+ (int)((e >> 3) & 1), (int)((e >> 4) & 1), (int)((e >> 5) & 1),
+ (int)((e >> 6) & 1), (int)((e >> 7) & 1));
+ return b;
+}
+
+/* Generic 4-level walk; returns final PA (masked) or UINT64_MAX on break. */
+static uint64_t walk4(uint64_t pml4_pa, uint64_t *raw_out)
+{
+ uint64_t e[4], pa = pml4_pa;
+ unsigned idx[4] = {idx_pml4, idx_pdpt, idx_pd, idx_pt};
+ int l;
+ for (l = 0; l < 4; l++) {
+ if (pa + PAGE_SIZE > (n_pages << PAGE_SHIFT))
+ return UINT64_MAX;
+ e[l] = rd64(pa + (uint64_t)idx[l] * 8);
+ if (raw_out) raw_out[l] = e[l];
+ if (!(e[l] & 1))
+ return UINT64_MAX;
+ if (l == 2 && (e[l] & 0x80)) { /* PS=1: 2 MiB page at PD level */
+ uint64_t base = entry_mask(e[l]) & ~((1ULL << 21) - 1);
+ return base | (va & ((1ULL << 21) - 1));
+ }
+ pa = entry_mask(e[l]);
+ }
+ return pa;
+}
+
+int main(int argc, char **argv)
+{
+ const char *path;
+ int fd;
+ struct stat st;
+ uint64_t i;
+ int32_t *head0, *next0, *head40, *next40, *pt_list;
+ uint64_t n_pt = 0;
+ int have_va = 0;
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s <ramfile> <target_pa> [va]\n", argv[0]);
+ return 2;
+ }
+ path = argv[1];
+ target_pa = strtoull(argv[2], NULL, 0) & PA_MASK;
+ if (argc >= 4) {
+ va = strtoull(argv[3], NULL, 0);
+ have_va = 1;
+ } else {
+ va = 0x4100000ULL;
+ }
+ /* indices: canonical 4-level split */
+ idx_pml4 = (unsigned)((va >> 39) & 0x1ff);
+ idx_pdpt = (unsigned)((va >> 30) & 0x1ff);
+ idx_pd = (unsigned)((va >> 21) & 0x1ff);
+ idx_pt = (unsigned)((va >> 12) & 0x1ff);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) { perror("open"); return 1; }
+ if (fstat(fd, &st) < 0) { perror("fstat"); return 1; }
+ if (st.st_size == 0) { fprintf(stderr, "empty file\n"); return 1; }
+ n_pages = (uint64_t)st.st_size >> PAGE_SHIFT;
+
+ ram = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (ram == MAP_FAILED) { perror("mmap"); return 1; }
+ madvise((void *)ram, (size_t)st.st_size, MADV_SEQUENTIAL);
+
+ fprintf(stderr, "ramfile=%s size=%llu bytes pages=%llu\n",
+ path, (unsigned long long)st.st_size, (unsigned long long)n_pages);
+ fprintf(stderr, "target_pa=0x%llx va=0x%llx idx: PML4[%u] PDPT[%u] PD[%u] PT[%u]\n",
+ (unsigned long long)target_pa, (unsigned long long)va,
+ idx_pml4, idx_pdpt, idx_pd, idx_pt);
+ fprintf(stderr, "PT entry offset=0x%x PD entry offset=0x%x\n",
+ idx_pt * 8, idx_pd * 8);
+
+ head0 = malloc(n_pages * sizeof(int32_t));
+ next0 = malloc(n_pages * sizeof(int32_t));
+ head40 = malloc(n_pages * sizeof(int32_t));
+ next40 = malloc(n_pages * sizeof(int32_t));
+ pt_list = malloc(n_pages * sizeof(int32_t));
+ if (!head0 || !next0 || !head40 || !next40 || !pt_list) {
+ fprintf(stderr, "out of memory\n"); return 1;
+ }
+ for (i = 0; i < n_pages; i++) { head0[i] = -1; head40[i] = -1; }
+
+ for (i = 0; i < n_pages; i++) {
+ const uint8_t *p = ram + (i << PAGE_SHIFT);
+ uint64_t e0, e40, ept, a;
+ memcpy(&e0, p + 0x000, 8);
+ memcpy(&e40, p + 0x040, 8);
+ memcpy(&ept, p + (uint64_t)idx_pt * 8, 8);
+
+ if (entry_mask(ept) == target_pa)
+ pt_list[n_pt++] = (int32_t)i;
+
+ a = entry_mask(e0);
+ if (a < (n_pages << PAGE_SHIFT) && (a & (PAGE_SIZE - 1)) == 0) {
+ uint64_t j = a >> PAGE_SHIFT;
+ next0[i] = head0[j];
+ head0[j] = (int32_t)i;
+ }
+ a = entry_mask(e40);
+ if (a < (n_pages << PAGE_SHIFT) && (a & (PAGE_SIZE - 1)) == 0) {
+ uint64_t j = a >> PAGE_SHIFT;
+ next40[i] = head40[j];
+ head40[j] = (int32_t)i;
+ }
+ }
+ madvise((void *)ram, (size_t)st.st_size, MADV_RANDOM);
+
+ fprintf(stderr, "pages whose entry[0x%x] == target: %llu\n",
+ idx_pt * 8, (unsigned long long)n_pt);
+
+ uint64_t n_chain = 0;
+ for (uint64_t t = 0; t < n_pt; t++) {
+ int32_t pt = pt_list[t];
+ int32_t pd;
+ for (pd = head40[pt]; pd >= 0; pd = next40[pd]) {
+ int32_t pdpt;
+ for (pdpt = head0[pd]; pdpt >= 0; pdpt = next0[pdpt]) {
+ int32_t pml4;
+ for (pml4 = head0[pdpt]; pml4 >= 0; pml4 = next0[pml4]) {
+ uint64_t pa_pml4 = (uint64_t)pml4 << PAGE_SHIFT;
+ uint64_t pa_pdpt = (uint64_t)pdpt << PAGE_SHIFT;
+ uint64_t pa_pd = (uint64_t)pd << PAGE_SHIFT;
+ uint64_t pa_pt = (uint64_t)pt << PAGE_SHIFT;
+ uint64_t raw[4] = {0}, fin;
+ fin = walk4(pa_pml4, raw);
+ n_chain++;
+ printf("\n=== candidate #%llu ===\n", (unsigned long long)n_chain);
+ printf("pdb (PML4 PA) = 0x%llx\n", (unsigned long long)pa_pml4);
+ printf("PML4[%u] @0x%llx+0x%03x = 0x%016llx -> PDPT 0x%llx [%s]\n",
+ idx_pml4, (unsigned long long)pa_pml4, idx_pml4 * 8,
+ (unsigned long long)raw[0], (unsigned long long)pa_pdpt,
+ bits(raw[0]));
+ printf("PDPT[%u] @0x%llx+0x%03x = 0x%016llx -> PD 0x%llx [%s]\n",
+ idx_pdpt, (unsigned long long)pa_pdpt, idx_pdpt * 8,
+ (unsigned long long)raw[1], (unsigned long long)pa_pd,
+ bits(raw[1]));
+ printf("PD[%u] @0x%llx+0x%03x = 0x%016llx -> PT 0x%llx [%s]\n",
+ idx_pd, (unsigned long long)pa_pd, idx_pd * 8,
+ (unsigned long long)raw[2], (unsigned long long)pa_pt,
+ bits(raw[2]));
+ printf("PT[%u] @0x%llx+0x%03x = 0x%016llx -> PA 0x%llx [%s]\n",
+ idx_pt, (unsigned long long)pa_pt, idx_pt * 8,
+ (unsigned long long)raw[3],
+ (unsigned long long)entry_mask(raw[3]), bits(raw[3]));
+ printf("walk_result = 0x%llx %s\n",
+ (unsigned long long)fin,
+ fin == target_pa ? "MATCH" : "MISMATCH");
+ }
+ }
+ }
+ }
+
+ /* Reference: the caller-supplied known pdb, if present in the file. */
+ if (have_va) {
+ uint64_t known = 0x11092a000ULL;
+ if (known < (n_pages << PAGE_SHIFT)) {
+ uint64_t raw[4] = {0}, fin = walk4(known, raw);
+ printf("\n=== reference walk from known pdb 0x%llx ===\n",
+ (unsigned long long)known);
+ printf("PML4[%u]=0x%016llx PDPT[%u]=0x%016llx PD[%u]=0x%016llx PT[%u]=0x%016llx\n",
+ idx_pml4, (unsigned long long)raw[0],
+ idx_pdpt, (unsigned long long)raw[1],
+ idx_pd, (unsigned long long)raw[2],
+ idx_pt, (unsigned long long)raw[3]);
+ printf("walk_result = 0x%llx\n", (unsigned long long)fin);
+ }
+ }
+
+ printf("\ntotal candidate chains: %llu\n", (unsigned long long)n_chain);
+
+ munmap((void *)ram, (size_t)st.st_size);
+ close(fd);
+ free(head0); free(next0); free(head40); free(next40); free(pt_list);
+ return 0;
+}
diff --git a/tools/debug/pdb_scan_gmmu.c b/tools/debug/pdb_scan_gmmu.c
new file mode 100644
index 0000000..f0221f6
--- /dev/null
+++ b/tools/debug/pdb_scan_gmmu.c
@@ -0,0 +1,330 @@
+/*
+ * pdb_scan_gmmu.c - read-only scanner for the GP10X/VER2 GMMU page-table
+ * chain that maps a GPU VA to a target PA inside a live QEMU pc.ram file.
+ *
+ * IMPORTANT: the GPU page tables are NOT x86-64. The format (gp10x family,
+ * Pascal..Ampere) is:
+ *
+ * PD3[48:47] -> PD2[46:38] -> PD1[37:29] -> PD0[28:21]
+ * PD0 is a 16-byte dual entry: big half at +0, small at +8
+ * PT[20:12] (4 KiB leaf)
+ *
+ * Entry address field: (entry >> 8) << 12 (aperture-dependent mask).
+ * Directory entries: ap bits 2:1 = 0 invalid, 1 vidmem, 2 sys-coherent,
+ * 3 sys-noncoherent. Leaf PTEs: 0 vidmem, 1 peer (invalid here), 2/3 sys.
+ * A directory entry has bit0 = IS_PTE = 0; a leaf has bit0 = VALID = 1.
+ *
+ * Also important: for this QEMU q35 guest the RAMBlock file offset is NOT
+ * equal to GPA above 4 GiB. Low RAM occupies GPA [0, low_ram) at file
+ * [0, low_ram); high RAM starts at GPA 4 GiB and maps to file offset
+ * low_ram (e820: low usable ends 0x7ffdf000, so low_ram = 0x80000000).
+ * Hence: file_off = GPA if GPA < low_ram, else GPA - 4G + low_ram.
+ *
+ * Build:
+ * cc -O2 -Wall -Wextra -o pdb_scan_gmmu pdb_scan_gmmu.c
+ * Run:
+ * sudo ./pdb_scan_gmmu /dev/shm/vgpu-guest-ram 0xafef8000 0x4100000
+ * sudo ./pdb_scan_gmmu /dev/shm/vgpu-guest-ram 0xa6c000 0x12006c000
+ *
+ * The tool is strictly read-only (PROT_READ/MAP_PRIVATE, no writes).
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define SYS_MASK 0x00003FFFFFFFFFFFULL
+#define VID_MASK 0x0000000001FFFFFFULL
+#define LOW_RAM 0x80000000ULL /* below-4G RAM block (override arg 4) */
+#define FOUR_G 0x100000000ULL
+
+static uint64_t va, target, low_ram = LOW_RAM;
+static uint64_t fs, np;
+static const uint8_t *ram;
+static unsigned i_pd3, i_pd2, i_pd1, i_pd0, i_pt;
+
+static inline uint64_t gpa_to_off(uint64_t gpa)
+{
+ if (gpa < low_ram) return gpa;
+ if (gpa >= FOUR_G && gpa - FOUR_G < fs - low_ram)
+ return gpa - FOUR_G + low_ram;
+ return UINT64_MAX;
+}
+static inline uint64_t off_to_gpa(uint64_t off)
+{
+ if (off < low_ram) return off;
+ return off + (FOUR_G - low_ram);
+}
+static inline uint64_t rd64(uint64_t gpa)
+{
+ uint64_t o = gpa_to_off(gpa), v;
+ if (o == UINT64_MAX || o + 8 > fs) return UINT64_MAX;
+ memcpy(&v, ram + o, 8);
+ return v;
+}
+static inline unsigned apof(uint64_t e) { return (unsigned)((e >> 1) & 3); }
+static inline uint64_t ent_addr(uint64_t e, unsigned ap)
+{
+ return ((e >> 8) & (ap == 1 ? VID_MASK : SYS_MASK)) << 12;
+}
+/* Directory entry -> child table. ap 0 invalid; ap 1 vid; ap 2/3 sys. */
+static int dir_child(uint64_t e, uint64_t *child)
+{
+ unsigned ap;
+ if (e == UINT64_MAX || (e & 1)) return -1;
+ ap = apof(e);
+ if (ap == 0) return -1;
+ *child = ent_addr(e, ap);
+ return 0;
+}
+/* Leaf -> backing PA. bit0 valid; ap 1 (peer) is not backed here. */
+static int leaf_pa(uint64_t e, uint64_t *pa)
+{
+ unsigned ap;
+ if (e == UINT64_MAX || !(e & 1)) return -1;
+ ap = apof(e);
+ if (ap == 1) return -1;
+ *pa = ent_addr(e, ap);
+ return 0;
+}
+static const char *apname(unsigned a)
+{
+ static const char *n[] = {"invalid", "vid", "sys-coh", "sys-nc"};
+ return a < 4 ? n[a] : "?";
+}
+static void dump_entry(const char *lvl, uint64_t table, unsigned idx, uint64_t e)
+{
+ printf(" %-4s table=0x%-10llx idx=0x%-3x e=0x%016llx V=%d ap=%u(%s)\n",
+ lvl, (unsigned long long)table, idx, (unsigned long long)e,
+ (int)(e & 1), apof(e), apname(apof(e)));
+}
+
+/* Full walk. raw[0..4]=PD3,PD2,PD1,PD0-small,PT; big64/64k handled. */
+static uint64_t walk(uint64_t pdb, uint64_t *raw, int *kind)
+{
+ uint64_t table = pdb, e, child;
+ unsigned shifts[3] = {47, 38, 29}, idx;
+ int l;
+
+ *kind = 0; /* 0=4K, 1=2M big, 2=64K */
+ for (l = 0; l < 3; l++) {
+ idx = (unsigned)((va >> shifts[l]) & 0x1ff);
+ if (l == 0) idx &= 3; /* 4 root entries */
+ e = rd64(table + 8 * idx);
+ raw[l] = e;
+ if (dir_child(e, &child)) return UINT64_MAX;
+ table = child;
+ if (gpa_to_off(table) == UINT64_MAX) return UINT64_MAX;
+ }
+ idx = (unsigned)((va >> 21) & 0xff); /* PD0 is 8 bits */
+ {
+ uint64_t big = rd64(table + 16 * idx);
+ uint64_t small = rd64(table + 16 * idx + 8);
+ uint64_t pa, t64;
+ raw[3] = small;
+ if (big == UINT64_MAX || small == UINT64_MAX) return UINT64_MAX;
+ /* big half: 2 MiB leaf (bit0=1) or 64 KiB table pointer (bit0=0) */
+ if ((big & 1) && !leaf_pa(big, &pa)) {
+ *kind = 1;
+ return pa | (va & ((1ULL << 21) - 1));
+ }
+ if (!dir_child(big, &t64) && gpa_to_off(t64) != UINT64_MAX) {
+ uint64_t e64 = rd64(t64 + 8 * ((va >> 16) & 0x1f));
+ if (!leaf_pa(e64, &pa)) { *kind = 2; return pa | (va & 0xffff); }
+ }
+ /* small half: 4 KiB page table */
+ if (dir_child(small, &child)) return UINT64_MAX;
+ table = child;
+ if (gpa_to_off(table) == UINT64_MAX) return UINT64_MAX;
+ }
+ idx = (unsigned)((va >> 12) & 0x1ff);
+ {
+ uint64_t pa;
+ e = rd64(table + 8 * idx);
+ raw[4] = e;
+ if (leaf_pa(e, &pa)) return UINT64_MAX;
+ return pa | (va & 0xfff);
+ }
+}
+
+/* Reverse chain: PT -> PD0 -> PD1 -> PD2 -> PD3, keyed by GPA page. */
+static void reverse_scan(void)
+{
+ uint64_t max_gpa_page = (off_to_gpa(fs) + 0xfff) >> 12;
+ int32_t *head0 = malloc((max_gpa_page + 1) * 4);
+ int32_t *next0 = malloc(np * 4);
+ int32_t *headpd = malloc((max_gpa_page + 1) * 4);
+ int32_t *nextpd = malloc(np * 4);
+ int32_t *pt_list = malloc(np * 4);
+ uint64_t i, n_pt = 0, n_chain = 0;
+
+ if (!head0 || !next0 || !headpd || !nextpd || !pt_list) {
+ fprintf(stderr, "reverse scan: out of memory\n");
+ return;
+ }
+ for (i = 0; i <= max_gpa_page; i++) { head0[i] = -1; headpd[i] = -1; }
+
+ for (i = 0; i < np; i++) {
+ const uint8_t *p = ram + (i << 12);
+ uint64_t e0, epd, ept, child, pa;
+ memcpy(&e0, p + 0, 8);
+ memcpy(&epd, p + 16 * i_pd0 + 8, 8);
+ memcpy(&ept, p + 8 * i_pt, 8);
+
+ if (!leaf_pa(ept, &pa) && pa == target)
+ pt_list[n_pt++] = (int32_t)i;
+
+ if (!dir_child(e0, &child) && (child >> 12) <= max_gpa_page) {
+ next0[i] = head0[child >> 12];
+ head0[child >> 12] = (int32_t)i;
+ }
+ if (!dir_child(epd, &child) && (child >> 12) <= max_gpa_page) {
+ nextpd[i] = headpd[child >> 12];
+ headpd[child >> 12] = (int32_t)i;
+ }
+ }
+ printf("\n[reverse scan] PT pages with leaf[0x%x] -> 0x%llx : %llu\n",
+ i_pt, (unsigned long long)target, (unsigned long long)n_pt);
+
+ for (uint64_t t = 0; t < n_pt; t++) {
+ int32_t pt = pt_list[t], pd0, pd1, pd2, pd3;
+ uint64_t pt_gp = off_to_gpa((uint64_t)pt << 12) >> 12;
+ for (pd0 = headpd[pt_gp]; pd0 >= 0; pd0 = nextpd[pd0]) {
+ uint64_t pd0_gp = off_to_gpa((uint64_t)pd0 << 12) >> 12;
+ for (pd1 = head0[pd0_gp]; pd1 >= 0; pd1 = next0[pd1]) {
+ uint64_t pd1_gp = off_to_gpa((uint64_t)pd1 << 12) >> 12;
+ for (pd2 = head0[pd1_gp]; pd2 >= 0; pd2 = next0[pd2]) {
+ uint64_t pd2_gp = off_to_gpa((uint64_t)pd2 << 12) >> 12;
+ for (pd3 = head0[pd2_gp]; pd3 >= 0; pd3 = next0[pd3]) {
+ uint64_t raw[5] = {0}, fin;
+ int kind;
+ fin = walk(off_to_gpa((uint64_t)pd3 << 12), raw, &kind);
+ if (fin != target) continue;
+ n_chain++;
+ printf("\n=== reverse chain #%llu (pdb GPA 0x%llx) ===\n",
+ (unsigned long long)n_chain,
+ (unsigned long long)off_to_gpa((uint64_t)pd3 << 12));
+ dump_entry("PD3", off_to_gpa((uint64_t)pd3 << 12), i_pd3, raw[0]);
+ dump_entry("PD2", ent_addr(raw[0], apof(raw[0])), i_pd2, raw[1]);
+ dump_entry("PD1", ent_addr(raw[1], apof(raw[1])), i_pd1, raw[2]);
+ dump_entry("PD0", ent_addr(raw[2], apof(raw[2])), i_pd0, raw[3]);
+ dump_entry("PT", ent_addr(raw[3], apof(raw[3])), i_pt, raw[4]);
+ printf(" walk result = 0x%llx (%s)\n",
+ (unsigned long long)fin,
+ kind == 1 ? "2M big" : kind == 2 ? "64K" : "4K");
+ }
+ }
+ }
+ }
+ }
+ printf("[reverse scan] matching chains: %llu\n", (unsigned long long)n_chain);
+ free(head0); free(next0); free(headpd); free(nextpd); free(pt_list);
+}
+
+typedef struct { uint64_t pa, pdb; int kind; } Hit;
+static int hit_cmp(const void *a, const void *b)
+{
+ const Hit *x = a, *y = b;
+ return x->pa < y->pa ? -1 : x->pa > y->pa ? 1 : 0;
+}
+
+/* Brute force: treat every 4K page as a candidate pdb, walk the VA, tally. */
+static void brute_force(void)
+{
+ Hit *h = malloc(np * sizeof(Hit));
+ uint64_t i, nh = 0, plausible = 0, matches = 0;
+ if (!h) { fprintf(stderr, "brute force: oom\n"); return; }
+
+ for (i = 0; i < np; i++) {
+ uint64_t pdb = off_to_gpa(i << 12), e0, child, fin;
+ int kind;
+ uint64_t raw[5] = {0};
+ e0 = rd64(pdb);
+ if (dir_child(e0, &child) || gpa_to_off(child) == UINT64_MAX) continue;
+ plausible++;
+ fin = walk(pdb, raw, &kind);
+ if (fin == UINT64_MAX) continue;
+ h[nh].pa = fin; h[nh].pdb = pdb; h[nh].kind = kind; nh++;
+ if (fin == target) {
+ matches++;
+ printf("\n=== brute-force MATCH pdb GPA 0x%llx -> 0x%llx (%s) ===\n",
+ (unsigned long long)pdb, (unsigned long long)fin,
+ kind == 1 ? "2M big" : kind == 2 ? "64K" : "4K");
+ dump_entry("PD3", pdb, i_pd3, raw[0]);
+ dump_entry("PD2", ent_addr(raw[0], apof(raw[0])), i_pd2, raw[1]);
+ dump_entry("PD1", ent_addr(raw[1], apof(raw[1])), i_pd1, raw[2]);
+ dump_entry("PD0", ent_addr(raw[2], apof(raw[2])), i_pd0, raw[3]);
+ dump_entry("PT", ent_addr(raw[3], apof(raw[3])), i_pt, raw[4]);
+ }
+ }
+ qsort(h, nh, sizeof(Hit), hit_cmp);
+ printf("\n[brute force] plausible pdbs=%llu valid walks=%llu target matches=%llu\n",
+ (unsigned long long)plausible, (unsigned long long)nh,
+ (unsigned long long)matches);
+ i = 0;
+ while (i < nh) {
+ uint64_t j = i;
+ while (j < nh && h[j].pa == h[i].pa) j++;
+ printf(" VA 0x%llx -> PA 0x%llx : %llu pdb(s), %s\n",
+ (unsigned long long)va, (unsigned long long)h[i].pa,
+ (unsigned long long)(j - i),
+ h[i].kind == 1 ? "2M big" : h[i].kind == 2 ? "64K" : "4K");
+ for (uint64_t k = i; k < j && k < i + 8; k++)
+ printf(" pdb 0x%llx\n", (unsigned long long)h[k].pdb);
+ if (j - i > 8) printf(" ...\n");
+ i = j;
+ }
+ free(h);
+}
+
+int main(int argc, char **argv)
+{
+ const char *path;
+ int fd;
+ struct stat st;
+
+ if (argc < 3) {
+ fprintf(stderr,
+ "usage: %s <ramfile> <target_pa> [va] [low_ram_bytes]\n"
+ " default va=0x4100000 low_ram=0x80000000\n", argv[0]);
+ return 2;
+ }
+ path = argv[1];
+ target = strtoull(argv[2], NULL, 0);
+ va = (argc >= 4) ? strtoull(argv[3], NULL, 0) : 0x4100000ULL;
+ if (argc >= 5) low_ram = strtoull(argv[4], NULL, 0);
+
+ i_pd3 = (unsigned)((va >> 47) & 3);
+ i_pd2 = (unsigned)((va >> 38) & 0x1ff);
+ i_pd1 = (unsigned)((va >> 29) & 0x1ff);
+ i_pd0 = (unsigned)((va >> 21) & 0xff);
+ i_pt = (unsigned)((va >> 12) & 0x1ff);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) { perror("open"); return 1; }
+ if (fstat(fd, &st) < 0) { perror("fstat"); return 1; }
+ fs = (uint64_t)st.st_size;
+ np = fs >> 12;
+ ram = mmap(NULL, (size_t)fs, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (ram == MAP_FAILED) { perror("mmap"); return 1; }
+
+ printf("file=%s size=%llu pages=%llu\n", path,
+ (unsigned long long)fs, (unsigned long long)np);
+ printf("target_pa=0x%llx va=0x%llx low_ram=0x%llx\n",
+ (unsigned long long)target, (unsigned long long)va,
+ (unsigned long long)low_ram);
+ printf("GMMU idx: PD3[%u] PD2[%u] PD1[%u] PD0[%u] PT[%u]\n",
+ i_pd3, i_pd2, i_pd1, i_pd0, i_pt);
+
+ reverse_scan();
+ brute_force();
+
+ munmap((void *)ram, (size_t)fs);
+ close(fd);
+ return 0;
+}