summaryrefslogtreecommitdiffstats
path: root/kernel/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/main.c')
-rw-r--r--kernel/main.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/kernel/main.c b/kernel/main.c
index 84a05ca..05a49b1 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -791,12 +791,34 @@ static ssize_t nv_vgpu_vfio_dev_write(struct vfio_device *vdev, const char __use
if (pos >= 0 && (u64)pos < vgpu->bars[0].size && count <= 8) {
u8 tmp[8];
ssize_t ret;
+ static u32 bar0_log;
if (!vgpu->bar_emul_ctx)
return -ENODEV;
if (copy_from_user(tmp, buf, count))
return -EFAULT;
+ if (bar0_log < 64 || (u64)pos == 0x88704) {
+ static u32 db_log;
+ u64 v = 0;
+
+ memcpy(&v, tmp, count);
+
+ if ((u64)pos == 0x88704) {
+ if (db_log < 512) {
+ pr_info("gsp-resman-vfio: DOORBELL write pos=0x%llx size=%zu val=0x%llx\n",
+ (u64)pos, count, v);
+ db_log++;
+ }
+ if (count == 4)
+ gsp_sema_service_doorbell((u32)v);
+ } else {
+ pr_info("gsp-resman-vfio: BAR0 write pos=0x%llx size=%zu val=0x%llx\n",
+ (u64)pos, count, v);
+ bar0_log++;
+ }
+ }
+
ret = nv_vgpu_bar_emul_write(vgpu, 0, (u64)pos, tmp, count);
if (ret)
return ret;
@@ -809,6 +831,17 @@ static ssize_t nv_vgpu_vfio_dev_write(struct vfio_device *vdev, const char __use
(u64)pos < (1ULL << 40) + vgpu->bars[1].size &&
vgpu->bars[1].vaddr && count <= 8) {
u64 bar1_off = (u64)pos - (1ULL << 40);
+ static u32 bar1_log;
+
+ if (bar1_log < 64) {
+ u64 v = 0;
+
+ memcpy(&v, buf, count);
+
+ pr_info("gsp-resman-vfio: BAR1 write off=0x%llx size=%zu val=0x%llx\n",
+ bar1_off, count, v);
+ bar1_log++;
+ }
if (copy_from_user(vgpu->bars[1].vaddr + bar1_off, buf, count))
return -EFAULT;
@@ -2215,6 +2248,41 @@ module_param_cb(sema_probe, &nv_vgpu_sema_probe_ops, NULL, 0200);
MODULE_PARM_DESC(sema_probe, "Dump sema-service map + slot values for a GPU VA");
/*
+ * Debug: inject a verified {GPU VA -> guest PA} translation into the
+ * semaphore service (used when the client's mapping cannot be discovered
+ * from guest structures):
+ * echo 0x<va>,0x<pa> > .../parameters/sema_map_add
+ */
+static int nv_vgpu_sema_map_add_set(const char *val, const struct kernel_param *kp)
+{
+ char buf[64];
+ char *comma, *comma2;
+ u64 va = 0, pa = 0, len = 0;
+
+ strscpy(buf, val, sizeof(buf));
+ comma = strchr(buf, ',');
+ if (!comma)
+ return -EINVAL;
+ *comma = '\0';
+ comma2 = strchr(comma + 1, ',');
+ if (comma2)
+ *comma2 = '\0';
+ if (kstrtoull(buf, 0, &va) || kstrtoull(comma + 1, 0, &pa))
+ return -EINVAL;
+ if (comma2 && kstrtoull(comma2 + 1, 0, &len))
+ return -EINVAL;
+
+ return gsp_sema_service_add_map(va, pa, len);
+}
+
+static const struct kernel_param_ops nv_vgpu_sema_map_add_ops = {
+ .set = nv_vgpu_sema_map_add_set,
+};
+
+module_param_cb(sema_map_add, &nv_vgpu_sema_map_add_ops, NULL, 0200);
+MODULE_PARM_DESC(sema_map_add, "Inject a GPU VA -> guest PA mapping (hex va,hex pa)");
+
+/*
* Debug: find page-table entries pointing at a physical address, in guest RAM
* and emulated vidmem:
* echo 0x12442b000 > .../parameters/scan_pt
@@ -2628,6 +2696,156 @@ module_param_cb(dump_vidmem, &nv_vgpu_dump_vidmem_ops, NULL, 0200);
MODULE_PARM_DESC(dump_vidmem, "Dump emulated vidmem range (hex offset,hex len)");
/*
+ * Debug: hex-dump or scan the BAR0 register shadow. Guest MMIO writes that go
+ * through the mapped shadow never reach the write callback, so this is the
+ * only way to observe registers the guest writes directly (e.g. the
+ * work-submission doorbell/notify registers, client region).
+ * echo 0x88700,0x100 > .../parameters/dump_bar0
+ * echo 0x00040022 > .../parameters/scan_bar0
+ */
+static int nv_vgpu_dump_bar0_set(const char *val, const struct kernel_param *kp)
+{
+ struct nv_vgpu_device *vgpu;
+ struct nv_vgpu_bar_emul_ctx *ctx;
+ unsigned long off = 0, len = 0;
+ char buf[64];
+ char *comma;
+
+ strscpy(buf, val, sizeof(buf));
+ comma = strchr(buf, ',');
+ if (comma)
+ *comma = '\0';
+ if (kstrtoul(buf, 0, &off))
+ return -EINVAL;
+ if (comma && kstrtoul(comma + 1, 0, &len))
+ return -EINVAL;
+ if (!len)
+ len = 0x100;
+ if (len > 0x1000)
+ len = 0x1000;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ u8 *base;
+ unsigned long o;
+
+ ctx = (struct nv_vgpu_bar_emul_ctx *)vgpu->bar_emul_ctx;
+ if (!ctx || !ctx->bar0_shadow)
+ continue;
+ base = (u8 *)ctx->bar0_shadow;
+ if (off + len > ctx->bar0_shadow_size)
+ continue;
+ for (o = 0; o < len; o += 64) {
+ u32 n = min_t(unsigned long, len - o, 64);
+
+ pr_info("gsp-resman-vfio: bar0[0x%lx..0x%lx]=%*phN\n",
+ off + o, off + o + n, (int)n, base + off + o);
+ }
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_dump_bar0_ops = {
+ .set = nv_vgpu_dump_bar0_set,
+};
+
+module_param_cb(dump_bar0, &nv_vgpu_dump_bar0_ops, NULL, 0200);
+MODULE_PARM_DESC(dump_bar0, "Dump BAR0 shadow range (hex offset,hex len)");
+
+static int nv_vgpu_scan_bar0_set(const char *val, const struct kernel_param *kp)
+{
+ struct nv_vgpu_device *vgpu;
+ struct nv_vgpu_bar_emul_ctx *ctx;
+ unsigned long needle = 0;
+ u32 hits = 0;
+
+ if (kstrtoul(val, 0, &needle))
+ return -EINVAL;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ u8 *base;
+ size_t size, o;
+
+ ctx = (struct nv_vgpu_bar_emul_ctx *)vgpu->bar_emul_ctx;
+ if (!ctx || !ctx->bar0_shadow)
+ continue;
+ base = (u8 *)ctx->bar0_shadow;
+ size = ctx->bar0_shadow_size;
+ for (o = 0; o + 4 <= size && hits < 64; o += 4) {
+ u32 v;
+
+ memcpy(&v, base + o, 4);
+ if (v == (u32)needle) {
+ pr_info("gsp-resman-vfio: bar0 scan: 0x%x at off 0x%zx\n",
+ v, o);
+ hits++;
+ }
+ }
+ pr_info("gsp-resman-vfio: bar0 scan: needle=0x%lx hits=%u\n",
+ needle, hits);
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_scan_bar0_ops = {
+ .set = nv_vgpu_scan_bar0_set,
+};
+
+module_param_cb(scan_bar0, &nv_vgpu_scan_bar0_ops, NULL, 0200);
+MODULE_PARM_DESC(scan_bar0, "Scan BAR0 shadow for a 32-bit value (hex)");
+
+/*
+ * Debug: find channel USERD blocks in the emulated vidmem. A USERD page has a
+ * GPGet at +0x88 and a GPPut at +0x8c; report every page whose GPPut is a
+ * plausible entry index (1..65535) with GPGet <= GPPut. This is how the real
+ * vidmem offset of a channel's control block is discovered when the offset
+ * carried in the allocation parameters is relative to a pool the emulation
+ * cannot see.
+ */
+static int nv_vgpu_find_userd_set(const char *val, const struct kernel_param *kp)
+{
+ struct nv_vgpu_device *vgpu;
+ int shown = 0;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ u8 *base = vgpu->bars[1].vaddr;
+ size_t size = vgpu->bars[1].size;
+ size_t off;
+
+ if (!base || !size)
+ continue;
+ for (off = 0; off + 0x90 <= size; off += 4) {
+ u32 get, put;
+
+ memcpy(&put, base + off + 0x8c, 4);
+ if (put < 1 || put > 65535)
+ continue;
+ memcpy(&get, base + off + 0x88, 4);
+ if (get > put)
+ continue;
+ if (shown < 32)
+ pr_info("gsp-resman-vfio: userd cand off=0x%zx get=%u put=%u\n",
+ off, get, put);
+ shown++;
+ }
+ pr_info("gsp-resman-vfio: userd scan: candidates=%d\n", shown);
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return 0;
+}
+
+static const struct kernel_param_ops nv_vgpu_find_userd_ops = {
+ .set = nv_vgpu_find_userd_set,
+};
+
+module_param_cb(find_userd, &nv_vgpu_find_userd_ops, NULL, 0200);
+MODULE_PARM_DESC(find_userd, "Find channel USERD blocks in emulated vidmem");
+
+/*
* Debug: write a u32 into the emulated vidmem (BAR1 backing) at an offset:
* echo 0x<offset>,0x<value> > .../parameters/poke_vidmem
*/
@@ -2754,5 +2972,56 @@ static const struct kernel_param_ops nv_vgpu_dump_guest_ops = {
module_param_cb(dump_guest, &nv_vgpu_dump_guest_ops, NULL, 0200);
MODULE_PARM_DESC(dump_guest, "Hex-dump a guest physical range (hex gpa,hex len)");
+/*
+ * Debug: deliver one MSI-X vector to the guest. QEMU wires each vector's
+ * eventfd via VFIO_DEVICE_SET_IRQS; signaling it makes QEMU raise the guest
+ * interrupt. The emulated GPU consumes work without raising the interrupts a
+ * real GPU would, so sessions use this to drive the guest's interrupt-driven
+ * progress paths (channel/runlist events) and find out which vector an event
+ * needs.
+ */
+static int nv_vgpu_inject_irq_set(const char *val, const struct kernel_param *kp)
+{
+ struct nv_vgpu_device *vgpu;
+ unsigned long idx = 0;
+ int ret = -ENODEV;
+
+ if (kstrtoul(val, 0, &idx) || idx >= NV_VGPU_MAX_IRQS)
+ return -EINVAL;
+
+ mutex_lock(&nv_vgpu_driver.lock);
+ list_for_each_entry(vgpu, &nv_vgpu_driver.devices, list) {
+ struct eventfd_ctx *trigger;
+
+ if (atomic_read(&vgpu->vfio_dev_open_count) <= 0)
+ continue;
+
+ if (vgpu->bar_emul_ctx && idx < NV_VGPU_MAX_IRQS) {
+ u32 bit = 1u << (idx % 32);
+
+ nv_vgpu_bar_emul_write(vgpu, 0,
+ GSP_EMU_MSIX_PBA_OFFSET +
+ (idx / 32) * 4,
+ &bit, sizeof(bit));
+ }
+
+ trigger = vgpu->irqs[idx].trigger;
+ if (trigger)
+ eventfd_signal(trigger);
+ pr_info("gsp-resman-vfio: inject_irq %lu (trigger %s)\n",
+ idx, trigger ? "fired" : "missing");
+ ret = 0;
+ }
+ mutex_unlock(&nv_vgpu_driver.lock);
+ return ret;
+}
+
+static const struct kernel_param_ops nv_vgpu_inject_irq_ops = {
+ .set = nv_vgpu_inject_irq_set,
+};
+
+module_param_cb(inject_irq, &nv_vgpu_inject_irq_ops, NULL, 0200);
+MODULE_PARM_DESC(inject_irq, "Deliver MSI-X vector N to the guest (debug)");
+
module_init(nv_vgpu_init);
module_exit(nv_vgpu_exit);