summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Zimmermann <tzimmermann@suse.de>2026-02-17 16:56:14 +0100
committerThomas Zimmermann <tzimmermann@suse.de>2026-02-20 14:38:19 +0100
commit0d847e16ca6c23a99a0228db6c2589d4b1898321 (patch)
tree9e5ff8b2b1913806ec88700d06bbab5d79090b4d
parent94918485d266ea2b578f36955c8cc88a33706e28 (diff)
firmware: google: framebuffer: Tie platform device to PCI hardware
Use the PCI device as parent of the system-framebuffer device instead of the coreboot device. Prevents SIGBUS or SIGSEG after hot-unplug of the PCI device while the framebuffer is active. The simple-framebuffer device depends on the PCI hardware, so this device needs to be its parent. The current coreboot parent is no longer needed after the system-framebuffer device has been created. On systems without PCI or if no PCI parent device could be found, the platform device hangs on the platform bus directly. The fix here is similar to code in sysfb, which contained that same bug. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Tzung-Bi Shih <tzungbi@kernel.org> Acked-by: Julius Werner <jwerner@chromium.org> Link: https://patch.msgid.link/20260217155836.96267-5-tzimmermann@suse.de
-rw-r--r--drivers/firmware/google/framebuffer-coreboot.c82
1 files changed, 77 insertions, 5 deletions
diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c
index 767515a30a52..07e9c7be94fa 100644
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
#include <linux/sysfb.h>
@@ -21,14 +22,71 @@
#define CB_TAG_FRAMEBUFFER 0x12
+#if defined(CONFIG_PCI)
+static bool framebuffer_pci_dev_is_enabled(struct pci_dev *pdev)
+{
+ /*
+ * TODO: Try to integrate this code into the PCI subsystem
+ */
+ int ret;
+ u16 command;
+
+ ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (ret != PCIBIOS_SUCCESSFUL)
+ return false;
+ if (!(command & PCI_COMMAND_MEMORY))
+ return false;
+ return true;
+}
+
+static struct pci_dev *framebuffer_parent_pci_dev(struct resource *res)
+{
+ struct pci_dev *pdev = NULL;
+ const struct resource *r = NULL;
+
+ while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev)))
+ r = pci_find_resource(pdev, res);
+
+ if (!r || !pdev)
+ return NULL; /* not found; not an error */
+
+ if (!framebuffer_pci_dev_is_enabled(pdev)) {
+ pci_dev_put(pdev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return pdev;
+}
+#else
+static struct pci_dev *framebuffer_parent_pci_dev(struct resource *res)
+{
+ return NULL;
+}
+#endif
+
+static struct device *framebuffer_parent_dev(struct resource *res)
+{
+ struct pci_dev *pdev;
+
+ pdev = framebuffer_parent_pci_dev(res);
+ if (IS_ERR(pdev))
+ return ERR_CAST(pdev);
+ else if (pdev)
+ return &pdev->dev;
+
+ return NULL;
+}
+
static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
static int framebuffer_probe(struct coreboot_device *dev)
{
int i;
struct lb_framebuffer *fb = &dev->framebuffer;
+ struct device *parent;
struct platform_device *pdev;
struct resource res;
+ int ret;
struct simplefb_platform_data pdata = {
.width = fb->x_resolution,
.height = fb->y_resolution,
@@ -57,6 +115,10 @@ static int framebuffer_probe(struct coreboot_device *dev)
if (res.end <= res.start)
return -EINVAL;
+ parent = framebuffer_parent_dev(&res);
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
if (fb->bits_per_pixel == formats[i].bits_per_pixel &&
fb->red_mask_pos == formats[i].red.offset &&
@@ -67,17 +129,27 @@ static int framebuffer_probe(struct coreboot_device *dev)
fb->blue_mask_size == formats[i].blue.length)
pdata.format = formats[i].name;
}
- if (!pdata.format)
- return -ENODEV;
+ if (!pdata.format) {
+ ret = -ENODEV;
+ goto out_put_device_parent;
+ }
- pdev = platform_device_register_resndata(&dev->dev,
+ pdev = platform_device_register_resndata(parent,
"simple-framebuffer", 0,
&res, 1, &pdata,
sizeof(pdata));
- if (IS_ERR(pdev))
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
pr_warn("coreboot: could not register framebuffer\n");
+ goto out_put_device_parent;
+ }
+
+ ret = 0;
- return PTR_ERR_OR_ZERO(pdev);
+out_put_device_parent:
+ if (parent)
+ put_device(parent);
+ return ret;
}
static const struct coreboot_device_id framebuffer_ids[] = {