diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-06 19:45:12 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-06 19:46:02 -0400 |
| commit | 315bec1caf0d949d83cb31e2908590ed6665b19f (patch) | |
| tree | a443b3dfb6d71592800751eaa1dd61424525464f /kernel/gsp_phys_booter.c | |
| parent | fb658fb1101aee3f2597ff22bdf33b0d3bfb0b0a (diff) | |
| download | open-iov-315bec1caf0d949d83cb31e2908590ed6665b19f.tar.gz open-iov-315bec1caf0d949d83cb31e2908590ed6665b19f.zip | |
vgsp: add read-only gsp-phys-reg-probe debugfs register snapshot
Add a read-only BAR0 register-liveness probe at
/sys/kernel/debug/vgsp/gsp-phys-reg-probe that prints "name addr value"
for the PMC chip-id/clock gate, PGRAPH status/fault, SKED/HWW and
FECS/GPCCS falcon state, the front-end QMD virtual-window controls and
the GSP context registers, followed by decoded field lines.
Read-only (readl only); additive to gsp_phys_boot_debugfs_init(); no
ladder logic changed. Used to distinguish a clock/power-gated engine
from a halted or running one during a physical cycle.
Diffstat (limited to 'kernel/gsp_phys_booter.c')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 232 |
1 files changed, 231 insertions, 1 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 0bafced..ca9559d 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -17126,6 +17126,222 @@ static const struct file_operations gsp_phys_guest_ram_fops = { .llseek = no_llseek, }; +/* ============================================================ + * Read-only register-liveness probe (debugfs): gsp-phys-reg-probe + * + * One-shot, side-effect-free 32-bit MMIO snapshot of the BAR0 blocks that + * answer "is the graphics front-end / FECS / SKED present and alive": + * chip-level PMC access sanity, PGRAPH status, the FECS and GPCCS falcon + * cores and the front-end QMD-window controls. It only reads; it never + * writes and never touches ladder state. + * + * The device must be in D0 with memory space enabled (as during a GSP + * cycle); do not read after D3. Zero is a legitimate value for several of + * these registers (no QMD window programmed, no context resident), so the + * reading relies on the decoded lines and the PMC access-sanity word, not + * on any single raw value. + * ============================================================ */ + +static u32 gsp_phys_reg_probe_line(struct seq_file *m, void __iomem *bar0, + const char *name, u32 addr) +{ + u32 value = readl(bar0 + addr); + + seq_printf(m, "%-30s 0x%08x 0x%08x\n", name, addr, value); + return value; +} + +static const char *gsp_phys_reg_probe_falcon_cpu(u32 cpuctl) +{ + if (cpuctl & BIT(4)) + return "halted"; + if (cpuctl & BIT(5)) + return "stopped"; + return "running"; +} + +static int gsp_phys_reg_probe_show(struct seq_file *m, void *unused) +{ + void __iomem *bar0 = gsp_phys_gpu_bar0(); + u32 boot0, boot1, pmc_enable; + u32 pgraph_status, pgraph_status1, pgraph_engine, pgraph_activity0; + u32 pgraph_grfifo, fe_hww_esr, fe_chip_def, fe_go_idle; + u32 trap_addr, trap_lo, trap_hi; + u32 sked_hww_esr, sked_cwd0, sked_cwd1, sked_activity; + u32 qmdctl0, qmdctl1, qmdctl2, qmdctl3, qmdbasea0, qmdbaseb0; + u32 fecs_irq, fecs_cpuctl, fecs_hwcfg, fecs_hwcfg2, fecs_bootvec; + u32 fecs_os, fecs_cur, fecs_new, fecs_st1, fecs_stfe, fecs_host; + u32 fecs_mb0, fecs_mb1; + u32 gpccs_irq, gpccs_cpuctl, gpccs_hwcfg, gpccs_hwcfg2; + u32 gpccs_bootvec, gpccs_os, gpccs_mb0, gpccs_mb1; + u32 gsp_cpuctl, gsp_riscv, gsp_wpr2_lo, gsp_wpr2_hi; + + if (!bar0) + return -ENODEV; + + seq_puts(m, "# read-only BAR0 register-liveness snapshot (D0 only)\n"); + + boot0 = gsp_phys_reg_probe_line(m, bar0, "PMC_BOOT_0", 0x00000000u); + boot1 = gsp_phys_reg_probe_line(m, bar0, "PMC_BOOT_1", 0x00000004u); + pmc_enable = gsp_phys_reg_probe_line(m, bar0, "PMC_ENABLE", + 0x00000200u); + pgraph_status = gsp_phys_reg_probe_line(m, bar0, "PGRAPH_STATUS", + 0x00400700u); + pgraph_status1 = gsp_phys_reg_probe_line(m, bar0, "PGRAPH_STATUS1", + 0x00400604u); + pgraph_engine = gsp_phys_reg_probe_line(m, bar0, + "PGRAPH_ENGINE_STATUS", + 0x0040060cu); + pgraph_activity0 = gsp_phys_reg_probe_line(m, bar0, + "PGRAPH_ACTIVITY0", + 0x00400380u); + pgraph_grfifo = gsp_phys_reg_probe_line(m, bar0, + "PGRAPH_GRFIFO_STATUS", + 0x00400504u); + trap_addr = gsp_phys_reg_probe_line(m, bar0, "PGRAPH_TRAPPED_ADDR", + 0x00400704u); + trap_lo = gsp_phys_reg_probe_line(m, bar0, "PGRAPH_TRAPPED_DATA_LO", + 0x00400708u); + trap_hi = gsp_phys_reg_probe_line(m, bar0, "PGRAPH_TRAPPED_DATA_HI", + 0x0040070cu); + fe_hww_esr = gsp_phys_reg_probe_line(m, bar0, "FE_HWW_ESR", + 0x00404000u); + fe_chip_def = gsp_phys_reg_probe_line(m, bar0, "FE_CHIP_DEF_INFO", + 0x00404030u); + fe_go_idle = gsp_phys_reg_probe_line(m, bar0, "FE_GO_IDLE_INFO", + 0x00404194u); + sked_hww_esr = gsp_phys_reg_probe_line(m, bar0, "SKED_HWW_ESR", + 0x00407020u); + sked_activity = gsp_phys_reg_probe_line(m, bar0, "SKED_ACTIVITY", + 0x00407054u); + sked_cwd0 = gsp_phys_reg_probe_line(m, bar0, + "SKED_DEBUG_CWD_TABLE_0", + 0x00407060u); + sked_cwd1 = gsp_phys_reg_probe_line(m, bar0, + "SKED_DEBUG_CWD_TABLE_1", + 0x00407064u); + fecs_irq = gsp_phys_reg_probe_line(m, bar0, "FECS_IRQSTAT", + 0x00409008u); + fecs_cpuctl = gsp_phys_reg_probe_line(m, bar0, "FECS_CPUCTL", + 0x00409100u); + fecs_hwcfg = gsp_phys_reg_probe_line(m, bar0, "FECS_HWCFG", + 0x00409108u); + fecs_hwcfg2 = gsp_phys_reg_probe_line(m, bar0, "FECS_HWCFG2", + 0x004090f4u); + fecs_bootvec = gsp_phys_reg_probe_line(m, bar0, "FECS_BOOTVEC", + 0x00409104u); + fecs_os = gsp_phys_reg_probe_line(m, bar0, "FECS_OS", 0x00409080u); + fecs_cur = gsp_phys_reg_probe_line(m, bar0, "FECS_CURRENT_CTX", + 0x00409b00u); + fecs_new = gsp_phys_reg_probe_line(m, bar0, "FECS_NEW_CTX", + 0x00409b04u); + fecs_st1 = gsp_phys_reg_probe_line(m, bar0, "FECS_CTXSW_STATUS_1", + 0x00409400u); + fecs_stfe = gsp_phys_reg_probe_line(m, bar0, + "FECS_CTXSW_STATUS_FE_0", + 0x00409c00u); + fecs_host = gsp_phys_reg_probe_line(m, bar0, "FECS_HOST_INT_STATUS", + 0x00409c18u); + fecs_mb0 = gsp_phys_reg_probe_line(m, bar0, "FECS_MAILBOX0", + 0x00409040u); + fecs_mb1 = gsp_phys_reg_probe_line(m, bar0, "FECS_MAILBOX1", + 0x00409044u); + gpccs_irq = gsp_phys_reg_probe_line(m, bar0, "GPCCS_IRQSTAT", + 0x0041a008u); + gpccs_cpuctl = gsp_phys_reg_probe_line(m, bar0, "GPCCS_CPUCTL", + 0x0041a100u); + gpccs_hwcfg = gsp_phys_reg_probe_line(m, bar0, "GPCCS_HWCFG", + 0x0041a108u); + gpccs_hwcfg2 = gsp_phys_reg_probe_line(m, bar0, "GPCCS_HWCFG2", + 0x0041a0f4u); + gpccs_bootvec = gsp_phys_reg_probe_line(m, bar0, "GPCCS_BOOTVEC", + 0x0041a104u); + gpccs_os = gsp_phys_reg_probe_line(m, bar0, "GPCCS_OS", + 0x0041a080u); + gpccs_mb0 = gsp_phys_reg_probe_line(m, bar0, "GPCCS_MAILBOX0", + 0x0041a040u); + gpccs_mb1 = gsp_phys_reg_probe_line(m, bar0, "GPCCS_MAILBOX1", + 0x0041a044u); + qmdctl0 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_CONTROL_0", + 0x0040a500u); + qmdctl1 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_CONTROL_1", + 0x0040a504u); + qmdctl2 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_CONTROL_2", + 0x0040a508u); + qmdctl3 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_CONTROL_3", + 0x0040a50cu); + qmdbasea0 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_BASEA_0", + 0x0040a300u); + qmdbaseb0 = gsp_phys_reg_probe_line(m, bar0, "FE_QMD_VIRT_BASEB_0", + 0x0040a400u); + gsp_cpuctl = gsp_phys_reg_probe_line(m, bar0, "GSP_CPUCTL", + 0x00110100u); + gsp_riscv = gsp_phys_reg_probe_line(m, bar0, "GSP_RISCV_STATUS", + 0x00111240u); + gsp_wpr2_lo = gsp_phys_reg_probe_line(m, bar0, "GSP_WPR2_ADDR_LO", + 0x001fa824u); + gsp_wpr2_hi = gsp_phys_reg_probe_line(m, bar0, "GSP_WPR2_ADDR_HI", + 0x001fa828u); + + seq_printf(m, + "decode: pmc boot0 arch=0x%02x impl=0x%x major=0x%x minor=0x%x (this TU104 reads 0x164000a1; 0/0xffffffff = BAR0 not responding)\n", + (boot0 >> 24) & 0x1fu, (boot0 >> 20) & 0xfu, + (boot0 >> 4) & 0xfu, boot0 & 0xfu); + seq_printf(m, + "decode: pmc_enable pgraph=%u (bit12; 0 = PGRAPH clock gated) pfifo=%u (bit8) pwr=%u (bit13)\n", + (pmc_enable >> 12) & 1u, (pmc_enable >> 8) & 1u, + (pmc_enable >> 13) & 1u); + seq_printf(m, + "decode: pgraph engine_status=%s status=0x%08x gpc_busy=0x%08x activity0=0x%08x grfifo=0x%08x\n", + (pgraph_engine & 1u) ? "busy" : "idle", pgraph_status, + pgraph_status1, pgraph_activity0, pgraph_grfifo); + seq_printf(m, + "decode: fe chip_def max_veid=%u hww_esr=0x%08x go_idle=0x%08x trapped=0x%08x/0x%08x/0x%08x\n", + fe_chip_def & 0xfffu, fe_hww_esr, fe_go_idle, trap_addr, + trap_lo, trap_hi); + seq_printf(m, + "decode: sked hww_esr=0x%08x activity=0x%08x cwd_table0=0x%08x cwd_table1=0x%08x\n", + sked_hww_esr, sked_activity, sked_cwd0, sked_cwd1); + seq_printf(m, + "decode: fecs cpu=%s irqstat=0x%08x hwcfg=0x%08x imem=%u dmem=%u hwcfg2=0x%08x bootvec=0x%08x os=0x%08x\n", + gsp_phys_reg_probe_falcon_cpu(fecs_cpuctl), fecs_irq, + fecs_hwcfg, fecs_hwcfg & 0x1ffu, (fecs_hwcfg >> 9) & 0x1ffu, + fecs_hwcfg2, fecs_bootvec, fecs_os); + seq_printf(m, + "decode: fecs current_ctx target=%u ptr=0x%07x new_ctx=0x%08x st1=0x%08x fe0=0x%08x host_int=0x%08x mb=0x%08x/0x%08x\n", + (fecs_cur >> 28) & 3u, fecs_cur & 0x0fffffffu, fecs_new, + fecs_st1, fecs_stfe, fecs_host, fecs_mb0, fecs_mb1); + seq_printf(m, + "decode: gpccs cpu=%s irqstat=0x%08x hwcfg=0x%08x imem=%u dmem=%u hwcfg2=0x%08x bootvec=0x%08x os=0x%08x mb=0x%08x/0x%08x\n", + gsp_phys_reg_probe_falcon_cpu(gpccs_cpuctl), gpccs_irq, + gpccs_hwcfg, gpccs_hwcfg & 0x1ffu, + (gpccs_hwcfg >> 9) & 0x1ffu, gpccs_hwcfg2, gpccs_bootvec, + gpccs_os, gpccs_mb0, gpccs_mb1); + seq_printf(m, + "decode: qmd_virt ctl=0x%08x/0x%08x/0x%08x/0x%08x i2m=%u iq2m=%u pcas=%u basea0=0x%08x baseb0=0x%08x\n", + qmdctl0, qmdctl1, qmdctl2, qmdctl3, + (qmdctl0 >> 8) & 1u, (qmdctl0 >> 16) & 1u, + (qmdctl0 >> 24) & 1u, qmdbasea0, qmdbaseb0); + seq_printf(m, + "decode: gsp cpuctl=0x%08x riscv=0x%08x wpr2=0x%08x:0x%08x\n", + gsp_cpuctl, gsp_riscv, gsp_wpr2_hi, gsp_wpr2_lo); + + return 0; +} + +static int gsp_phys_reg_probe_open(struct inode *inode, struct file *file) +{ + return single_open(file, gsp_phys_reg_probe_show, NULL); +} + +static const struct file_operations gsp_phys_reg_probe_fops = { + .owner = THIS_MODULE, + .open = gsp_phys_reg_probe_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int gsp_phys_boot_debugfs_init(void) { struct dentry *file; @@ -17250,7 +17466,21 @@ int gsp_phys_boot_debugfs_init(void) err); return (int)err; } - pr_info("vgsp-boot: debugfs files /sys/kernel/debug/vgsp/{gsp-phys-state read-only, gsp-phys-log-dump read-only, gsp-phys-status-drain read-only, gsp-phys-unload-rpc write-only, gsp-phys-rm-probe write-only, gsp-phys-rm-call write-only, gsp-phys-guest-gpa write-only, gsp-phys-guest-ram write-only} ready\n"); + file = debugfs_create_file("gsp-phys-reg-probe", 0444, + gsp_phys_debugfs_root, NULL, + &gsp_phys_reg_probe_fops); + if (IS_ERR_OR_NULL(file)) { + long err = IS_ERR(file) ? PTR_ERR(file) : -ENODEV; + + /* + * The register-liveness probe is diagnostic; the triggers, + * log dump, status drain and state snapshot stay registered. + */ + pr_warn("vgsp-boot: debugfs file gsp-phys-reg-probe creation failed: %ld (triggers, log dump, status drain and state snapshot stay available)\n", + err); + return (int)err; + } + pr_info("vgsp-boot: debugfs files /sys/kernel/debug/vgsp/{gsp-phys-state read-only, gsp-phys-log-dump read-only, gsp-phys-status-drain read-only, gsp-phys-reg-probe read-only, gsp-phys-unload-rpc write-only, gsp-phys-rm-probe write-only, gsp-phys-rm-call write-only, gsp-phys-guest-gpa write-only, gsp-phys-guest-ram write-only} ready\n"); return 0; } |
