From f7980444a921a0b5027e05a74f7535138e816a3d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Wed, 23 Sep 2026 12:26:03 -0400 Subject: kernel: service BAR0/BAR1 region reads and extended config-space writes Two more retry loops surfaced once the guest driver got past the CE binding: - BAR0 region reads returned -EINVAL ("would be handled by mmap"), so a QEMU-trapped read of BAR0+0x88084 failed and the driver polled it forever. Serve reads from the BAR0 shadow/trap handler (and mirror BAR1 reads from the emulated vidmem backing). - Config-space writes above offset 0xFF were refused with -EINVAL, so the driver's write to extended config 0x704 (PCIe device control area) never took effect and it retried. Accept and store extended-space writes; keep the legacy identity registers fixed. --- kernel/main.c | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'kernel/main.c') diff --git a/kernel/main.c b/kernel/main.c index 45c34ab..2ec7ab9 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -725,10 +725,38 @@ static ssize_t nv_vgpu_vfio_dev_read(struct vfio_device *vdev, char __user *buf, return to_read; } - /* BAR0 region: VFIO_PCI_INDEX_TO_OFFSET(VFIO_PCI_BAR0_REGION_INDEX) = 0x0 */ - if (pos >= 0x0 && pos < 0x0 + (256 * 1024 * 1024)) { - /* This would be handled by mmap, not read */ - return -EINVAL; + /* BAR0 region (register space). Most guest accesses hit the mmap'd shadow + * directly, but region reads also arrive here. They must be served from + * the shadow (and the trap table) - refusing them makes the guest driver + * poll a register forever. */ + if (pos >= 0 && (u64)pos < vgpu->bars[0].size && count <= 8) { + u8 tmp[8]; + int ret; + + if (!vgpu->bar_emul_ctx) + return -ENODEV; + if (count > sizeof(tmp)) + return -EINVAL; + + ret = nv_vgpu_bar_emul_read(vgpu, 0, (u64)pos, tmp, count); + if (ret) + return ret; + if (copy_to_user(buf, tmp, count)) + return -EFAULT; + *ppos += count; + return count; + } + + /* BAR1 region (VRAM): read from the emulated vidmem backing. */ + if (pos >= (1ULL << 40) && + (u64)pos < (1ULL << 40) + vgpu->bars[1].size && + vgpu->bars[1].vaddr && count <= 8) { + u64 bar1_off = (u64)pos - (1ULL << 40); + + if (copy_to_user(buf, vgpu->bars[1].vaddr + bar1_off, count)) + return -EFAULT; + *ppos += count; + return count; } return 0; @@ -1855,11 +1883,11 @@ static ssize_t nv_vgpu_config_write(struct nv_vgpu_device *vgpu, const char __us size_t written = 0; u8 val; - if (pos >= 256) + if (pos >= 4096) return -EINVAL; - if (pos + count > 256) - count = 256 - pos; + if (pos + count > 4096) + count = 4096 - pos; /* Handle specific writable registers */ while (count--) { @@ -1902,8 +1930,14 @@ static ssize_t nv_vgpu_config_write(struct nv_vgpu_device *vgpu, const char __us case 0xa4: case 0xa5: /* Link Control 2 */ vgpu->pci_config[pos] = val; break; + case 0x100 ... 0xfff: + /* Extended capability space: accept and store. The guest + * driver programs registers here (e.g. 0x704) and retries + * forever if the write is refused. */ + vgpu->pci_config[pos] = val; + break; default: - /* Most config space is read-only for guest */ + /* Legacy identity/read-only registers stay fixed */ break; } pos++; -- cgit v1.2.3