summaryrefslogtreecommitdiffstats
path: root/kernel/bar_emulation.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-13 18:36:21 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-13 18:36:21 -0400
commit00e54c13c7c9030c4d5f7151467f2d85a3730e29 (patch)
tree17a45239df8d2e4912ff3697ed06e85a10b969de /kernel/bar_emulation.c
downloadopen-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.tar.gz
open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.zip
Initial commit: Open-IOV vGPU-IO complete stack
- Firmware decompilation: GSP 570.144W (28.5MB ELF, RV64IMAC), 132 decompiled C files - Kernel modules: nvidia.ko (34MB, OpenRM), open-iov-vfio.ko (2.8MB, VFIO mdev), gsp-vgpu.ko (1.8MB, guest driver) - Userspace: libiov.so/libvgpu.so (76KB), example_vgpu (28KB), rpc_emulator.c - PCI config space emulation (256B with PM, MSI, MSI-X, PCIe caps) - 12 RPC types fully mapped from firmware decompilation - BAR trap, WPR, scheduling, notification, migration emulation - Helper scripts: load_vgpu_stack, create_mdev, launch_qemu_vgpu, test_vgpu_stack, setup_guest_driver, unload_vgpu_stack - Architecture documentation - Wiki: 10 technical documents - Git LFS for large binaries (.ko, .bin, .iso, .img, .o, ghidra repos) - nvidia-open-gpu-kernel-modules as submodule
Diffstat (limited to 'kernel/bar_emulation.c')
-rw-r--r--kernel/bar_emulation.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/kernel/bar_emulation.c b/kernel/bar_emulation.c
new file mode 100644
index 0000000..17904fe
--- /dev/null
+++ b/kernel/bar_emulation.c
@@ -0,0 +1,267 @@
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+
+#include "nvidia-vgpu-vfio.h"
+#include "bar_emulation.h"
+#include "gsp_comm.h"
+
+#define NV_VGPU_BAR0_TRAP_REGIONS 64
+
+static const struct {
+ u32 offset;
+ u32 size;
+ const char *name;
+} nv_vgpu_bar0_traps[] = {
+ { 0x000000, 0x1000, "PMC" },
+ { 0x001000, 0x1000, "PRAMIN" },
+ { 0x002000, 0x1000, "PCR" },
+ { 0x003000, 0x1000, "PRAMDAC" },
+ { 0x004000, 0x1000, "PVIO" },
+ { 0x005000, 0x1000, "PFIFO" },
+ { 0x006000, 0x1000, "PGRAPH" },
+ { 0x007000, 0x1000, "PTIMER" },
+ { 0x008000, 0x1000, "PFUSE" },
+ { 0x009000, 0x1000, "PBUS" },
+ { 0x00A000, 0x1000, "PCOP" },
+ { 0x00B000, 0x1000, "PVP" },
+ { 0x00C000, 0x1000, "PCE" },
+ { 0x00D000, 0x1000, "PDISP" },
+ { 0x00E000, 0x1000, "PNVDEC" },
+ { 0x00F000, 0x1000, "PNVENC" },
+ { 0x010000, 0x1000, "PSEC2" },
+ { 0x011000, 0x1000, "PMU" },
+ { 0x012000, 0x1000, "PGFX" },
+ { 0x013000, 0x1000, "PXVE" },
+ { 0x020000, 0x10000, "FIFO" },
+ { 0x040000, 0x10000, "GR" },
+ { 0x050000, 0x10000, "CE" },
+ { 0x060000, 0x10000, "NVDEC" },
+ { 0x070000, 0x10000, "NVENC" },
+ { 0x080000, 0x10000, "NVJPG" },
+ { 0x090000, 0x10000, "OFA" },
+ { 0x100000, 0x10000, "FB" },
+ { 0x110000, 0x10000, "LTC" },
+ { 0x120000, 0x10000, "GPC" },
+ { 0x130000, 0x10000, "TPC" },
+ { 0x140000, 0x10000, "ROP" },
+ { 0x150000, 0x10000, "HUB" },
+ { 0x160000, 0x10000, "NVLINK" },
+ { 0x170000, 0x10000, "GSP" },
+ { 0x180000, 0x10000, "RAP" },
+ { 0x190000, 0x10000, "SEC" },
+ { 0x1A0000, 0x10000, "PMU_RISCV" },
+ { 0x1B0000, 0x10000, "FALCON" },
+ { 0x1C0000, 0x10000, "XVE" },
+ { 0x1D0000, 0x10000, "DMCE" },
+ { 0x1E0000, 0x10000, "P2P" },
+ { 0x1F0000, 0x10000, "MISC" },
+};
+
+int nv_vgpu_bar_emul_init(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx;
+ int i, ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vgpu = vgpu;
+ ctx->bar0_shadow_size = NV_VGPU_BAR0_SIZE;
+ ctx->bar0_shadow = vzalloc(ctx->bar0_shadow_size);
+ if (!ctx->bar0_shadow) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ spin_lock_init(&ctx->shadow_lock);
+ ctx->trap_count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(nv_vgpu_bar0_traps); i++) {
+ if (ctx->trap_count >= 256)
+ break;
+ ctx->trap_regs[ctx->trap_count].offset = nv_vgpu_bar0_traps[i].offset;
+ ctx->trap_regs[ctx->trap_count].trapped = true;
+ ctx->trap_count++;
+ }
+
+ vgpu->bar_emul_ctx = ctx;
+ return 0;
+
+err_free:
+ kfree(ctx);
+ return ret;
+}
+
+void nv_vgpu_bar_emul_cleanup(struct nv_vgpu_device *vgpu)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+
+ if (!ctx)
+ return;
+
+ if (ctx->bar0_shadow)
+ vfree(ctx->bar0_shadow);
+
+ kfree(ctx);
+ vgpu->bar_emul_ctx = NULL;
+}
+
+int nv_vgpu_bar_trap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset, u32 size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ int i;
+
+ if (!ctx || bar_idx != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset) {
+ ctx->trap_regs[i].trapped = true;
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+ }
+ }
+
+ if (ctx->trap_count >= 256) {
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -ENOSPC;
+ }
+
+ ctx->trap_regs[ctx->trap_count].offset = offset;
+ ctx->trap_regs[ctx->trap_count].trapped = true;
+ ctx->trap_count++;
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+}
+
+int nv_vgpu_bar_untrap_register(struct nv_vgpu_device *vgpu, int bar_idx,
+ u32 offset)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ int i;
+
+ if (!ctx || bar_idx != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset) {
+ ctx->trap_regs[i].trapped = false;
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -ENOENT;
+}
+
+int nv_vgpu_bar_emul_read(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ u32 *val32 = val;
+ u64 *val64 = val;
+
+ if (!ctx || bar_idx != 0 || offset >= ctx->bar0_shadow_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ switch (size) {
+ case 4:
+ *val32 = *(u32 *)((u8 *)ctx->bar0_shadow + offset);
+ break;
+ case 8:
+ *val64 = *(u64 *)((u8 *)ctx->bar0_shadow + offset);
+ break;
+ default:
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return 0;
+}
+
+int nv_vgpu_bar_emul_write(struct nv_vgpu_device *vgpu, int bar_idx,
+ u64 offset, void *val, size_t size)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ unsigned long flags;
+ u32 *val32 = val;
+ u64 *val64 = val;
+ bool trapped = false;
+ int i;
+
+ if (!ctx || bar_idx != 0 || offset >= ctx->bar0_shadow_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->shadow_lock, flags);
+
+ for (i = 0; i < ctx->trap_count; i++) {
+ if (ctx->trap_regs[i].offset == offset && ctx->trap_regs[i].trapped) {
+ trapped = true;
+ ctx->trap_regs[i].value = (size == 4) ? *val32 : (u32)*val64;
+ ctx->trap_regs[i].is_write = true;
+ break;
+ }
+ }
+
+ switch (size) {
+ case 4:
+ *(u32 *)((u8 *)ctx->bar0_shadow + offset) = *val32;
+ break;
+ case 8:
+ *(u64 *)((u8 *)ctx->bar0_shadow + offset) = *val64;
+ break;
+ default:
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+ return -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&ctx->shadow_lock, flags);
+
+ if (trapped) {
+ nv_vgpu_dbg(vgpu, "BAR0 trap: offset=0x%llx, size=%zu, write=0x%x",
+ offset, size, (size == 4) ? *val32 : (u32)*val64);
+ nv_vgpu_gsp_rpc_bar_trap(vgpu, offset, 1, size, *(u64 *)val, 0, NULL);
+ }
+
+ return 0;
+}
+
+int nv_vgpu_bar_emul_handle_fault(struct nv_vgpu_device *vgpu,
+ unsigned long addr, bool write)
+{
+ struct nv_vgpu_bar_emul_ctx *ctx = vgpu->bar_emul_ctx;
+ struct nv_vgpu_bar *bar = &vgpu->bars[0];
+ u64 offset = addr - (unsigned long)bar->vaddr;
+ u32 val = 0;
+ int ret;
+
+ if (!ctx || !bar->mapped || !bar->trapped)
+ return -EINVAL;
+
+ if (write) {
+ ret = nv_vgpu_bar_emul_write(vgpu, 0, offset, &val, 4);
+ } else {
+ ret = nv_vgpu_bar_emul_read(vgpu, 0, offset, &val, 4);
+ }
+
+ return ret;
+} \ No newline at end of file