Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

video/aperture: optionally match the device in sysfb_disable()

In aperture_remove_conflicting_pci_devices(), we currently only
call sysfb_disable() on vga class devices. This leads to the
following problem when the pimary device is not VGA compatible:

1. A PCI device with a non-VGA class is the boot display
2. That device is probed first and it is not a VGA device so
sysfb_disable() is not called, but the device resources
are freed by aperture_detach_platform_device()
3. Non-primary GPU has a VGA class and it ends up calling sysfb_disable()
4. NULL pointer dereference via sysfb_disable() since the resources
have already been freed by aperture_detach_platform_device() when
it was called by the other device.

Fix this by passing a device pointer to sysfb_disable() and checking
the device to determine if we should execute it or not.

v2: Fix build when CONFIG_SCREEN_INFO is not set
v3: Move device check into the mutex
Drop primary variable in aperture_remove_conflicting_pci_devices()
Drop __init on pci sysfb_pci_dev_is_enabled()

Fixes: 5ae3716cfdcd ("video/aperture: Only remove sysfb on the default vga pci device")
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Helge Deller <deller@gmx.de>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240821191135.829765-1-alexander.deucher@amd.com

+19 -17
+13 -6
drivers/firmware/sysfb.c
··· 39 39 static DEFINE_MUTEX(disable_lock); 40 40 static bool disabled; 41 41 42 + static struct device *sysfb_parent_dev(const struct screen_info *si); 43 + 42 44 static bool sysfb_unregister(void) 43 45 { 44 46 if (IS_ERR_OR_NULL(pd)) ··· 54 52 55 53 /** 56 54 * sysfb_disable() - disable the Generic System Framebuffers support 55 + * @dev: the device to check if non-NULL 57 56 * 58 57 * This disables the registration of system framebuffer devices that match the 59 58 * generic drivers that make use of the system framebuffer set up by firmware. ··· 64 61 * Context: The function can sleep. A @disable_lock mutex is acquired to serialize 65 62 * against sysfb_init(), that registers a system framebuffer device. 66 63 */ 67 - void sysfb_disable(void) 64 + void sysfb_disable(struct device *dev) 68 65 { 66 + struct screen_info *si = &screen_info; 67 + 69 68 mutex_lock(&disable_lock); 70 - sysfb_unregister(); 71 - disabled = true; 69 + if (!dev || dev == sysfb_parent_dev(si)) { 70 + sysfb_unregister(); 71 + disabled = true; 72 + } 72 73 mutex_unlock(&disable_lock); 73 74 } 74 75 EXPORT_SYMBOL_GPL(sysfb_disable); 75 76 76 77 #if defined(CONFIG_PCI) 77 - static __init bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) 78 + static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) 78 79 { 79 80 /* 80 81 * TODO: Try to integrate this code into the PCI subsystem ··· 94 87 return true; 95 88 } 96 89 #else 97 - static __init bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) 90 + static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) 98 91 { 99 92 return false; 100 93 } 101 94 #endif 102 95 103 - static __init struct device *sysfb_parent_dev(const struct screen_info *si) 96 + static struct device *sysfb_parent_dev(const struct screen_info *si) 104 97 { 105 98 struct pci_dev *pdev; 106 99
+1 -1
drivers/of/platform.c
··· 592 592 * This can happen for example on DT systems that do EFI 593 593 * booting and may provide a GOP handle to the EFI stub. 594 594 */ 595 - sysfb_disable(); 595 + sysfb_disable(NULL); 596 596 of_platform_device_create(node, NULL, NULL); 597 597 of_node_put(node); 598 598 }
+3 -8
drivers/video/aperture.c
··· 293 293 * ask for this, so let's assume that a real driver for the display 294 294 * was already probed and prevent sysfb to register devices later. 295 295 */ 296 - sysfb_disable(); 296 + sysfb_disable(NULL); 297 297 298 298 aperture_detach_devices(base, size); 299 299 ··· 346 346 */ 347 347 int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name) 348 348 { 349 - bool primary = false; 350 349 resource_size_t base, size; 351 350 int bar, ret = 0; 352 351 353 - if (pdev == vga_default_device()) 354 - primary = true; 355 - 356 - if (primary) 357 - sysfb_disable(); 352 + sysfb_disable(&pdev->dev); 358 353 359 354 for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) { 360 355 if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) ··· 365 370 * that consumes the VGA framebuffer I/O range. Remove this 366 371 * device as well. 367 372 */ 368 - if (primary) 373 + if (pdev == vga_default_device()) 369 374 ret = __aperture_remove_legacy_vga_devices(pdev); 370 375 371 376 return ret;
+2 -2
include/linux/sysfb.h
··· 58 58 59 59 #ifdef CONFIG_SYSFB 60 60 61 - void sysfb_disable(void); 61 + void sysfb_disable(struct device *dev); 62 62 63 63 #else /* CONFIG_SYSFB */ 64 64 65 - static inline void sysfb_disable(void) 65 + static inline void sysfb_disable(struct device *dev) 66 66 { 67 67 } 68 68