From 2a93c9851b2bb38614fadd84aa674b7a5c8181c6 Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:34 +0900 Subject: [PATCH 1/3] PCI/VGA: Pass vga_get_uninterruptible() errors to userspace If VGA routing cannot be established, vga_get_uninterruptible() returns an error and does not increment the lock count. Return the error to the caller. Return before incrementing uc->io_cnt/mem_cnt so vga_arb_release() won't call vga_put() when userspace closes the handle. Signed-off-by: Simon Richter [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-2-Simon.Richter@hogyros.de --- drivers/pci/vgaarb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c index d9383bf541e7..22b2b6ebdefd 100644 --- a/drivers/pci/vgaarb.c +++ b/drivers/pci/vgaarb.c @@ -1134,6 +1134,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user *buf, char kbuf[64], *curr_pos; size_t remaining = count; + int err; int ret_val; int i; @@ -1165,7 +1166,11 @@ static ssize_t vga_arb_write(struct file *file, const char __user *buf, goto done; } - vga_get_uninterruptible(pdev, io_state); + err = vga_get_uninterruptible(pdev, io_state); + if (err) { + ret_val = err; + goto done; + } /* Update the client's locks lists */ for (i = 0; i < MAX_USER_CARDS; i++) { From 94555ea9a0488c5c1bba90242cfa149a84a8928d Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:35 +0900 Subject: [PATCH 2/3] PCI/VGA: Pass errors from pci_set_vga_state() up pci_set_vga_state() returns an error code, which so far has been ignored by the only caller, __vga_tryget(), so forward it to the caller. As the return type of __vga_tryget() is a pointer, wrap the error in ERR_PTR(). Signed-off-by: Simon Richter Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-3-Simon.Richter@hogyros.de --- drivers/pci/vgaarb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c index 22b2b6ebdefd..c360eee11dd9 100644 --- a/drivers/pci/vgaarb.c +++ b/drivers/pci/vgaarb.c @@ -215,6 +215,7 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, struct vga_device *conflict; unsigned int pci_bits; u32 flags = 0; + int err; /* * Account for "normal" resources to lock. If we decode the legacy, @@ -307,7 +308,9 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, if (change_bridge) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; - pci_set_vga_state(conflict->pdev, false, pci_bits, flags); + err = pci_set_vga_state(conflict->pdev, false, pci_bits, flags); + if (err) + return ERR_PTR(err); conflict->owns &= ~match; /* If we disabled normal decoding, reflect it in owns */ @@ -337,7 +340,9 @@ enable_them: if (wants & VGA_RSRC_LEGACY_MASK) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; - pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); + err = pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); + if (err) + return ERR_PTR(err); vgadev->owns |= wants; lock_them: @@ -455,6 +460,10 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) } conflict = __vga_tryget(vgadev, rsrc); spin_unlock_irqrestore(&vga_lock, flags); + if (IS_ERR(conflict)) { + rc = PTR_ERR(conflict); + break; + } if (conflict == NULL) break; From 5b6471fc72a42e6110adca54f46fd2c287dc49d4 Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:37 +0900 Subject: [PATCH 3/3] PCI/VGA: Fail pci_set_vga_state() if VGA decoding not supported PCI bridges are allowed to refuse activating VGA decoding, by simply ignoring attempts to set the bit that enables it, so after setting the bit, read it back to verify. One example of such a bridge is the root bridge in IBM PowerNV, but this is also useful for GPU passthrough into virtual machines, where it is difficult to set up routing for legacy IO through IOMMU. Signed-off-by: Simon Richter [bhelgaas: subject, add comment about VGA Enable writability] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-5-Simon.Richter@hogyros.de --- drivers/pci/pci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..52eb3ef0f487 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6197,6 +6197,18 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode, cmd &= ~PCI_BRIDGE_CTL_VGA; pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, cmd); + + + /* + * VGA Enable may not be writable if bridge doesn't + * support it. + */ + if (decode) { + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, + &cmd); + if (!(cmd & PCI_BRIDGE_CTL_VGA)) + return -EIO; + } } bus = bus->parent; }