diff mbox

vesa: Prevent loading if we conflict with a previously loaded driver

Message ID 1363619839-18532-1-git-send-email-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Chris Wilson March 18, 2013, 3:17 p.m. UTC
vesa is presumed to be the generic hardware agnostic fallback framebuffer
driver for standard VGA that conflicts with the real drivers. Those
drivers already check and remove vesafb if it is previously loaded, but
in the reverse case where vesafb is loaded afterwards, the VESA driver
will proceed to clobber hardware state and corrupt the displays.

References: https://bugs.launchpad.net/ubuntu/+source/xserver-xorg-video-intel/+bug/1156077
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/video/fbmem.c  |   31 +++++++++++++++++++++++++++++++
 drivers/video/vesafb.c |   22 ++++++++++++++++++++++
 include/linux/fb.h     |    2 ++
 3 files changed, 55 insertions(+)
diff mbox

Patch

diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 7c25408..786cec8 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1722,6 +1722,37 @@  void remove_conflicting_framebuffers(struct apertures_struct *a,
 }
 EXPORT_SYMBOL(remove_conflicting_framebuffers);
 
+bool has_conflicting_framebuffer(struct apertures_struct *a, bool primary)
+{
+	bool ret = false;
+	int i;
+
+	mutex_lock(&registration_lock);
+
+	for (i = 0 ; i < FB_MAX; i++) {
+		struct apertures_struct *gen_aper;
+
+		if (!registered_fb[i])
+			continue;
+
+		if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
+			continue;
+
+		gen_aper = registered_fb[i]->apertures;
+		if (fb_do_apertures_overlap(gen_aper, a) ||
+		    (primary && gen_aper && gen_aper->count &&
+		     gen_aper->ranges[0].base == VGA_FB_PHYS)) {
+			ret = true;
+			break;
+		}
+	}
+
+	mutex_unlock(&registration_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(has_conflicting_framebuffer);
+
 /**
  *	register_framebuffer - registers a frame buffer device
  *	@fb_info: frame buffer info structure
diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index 501b340..51adc4c1 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -226,6 +226,25 @@  static int __init vesafb_setup(char *options)
 	return 0;
 }
 
+static bool aperture_already_claimed(void)
+{
+	struct apertures_struct *ap;
+	bool ret;
+
+	ap = alloc_apertures(1);
+	if (!ap)
+		return true;
+
+	ap->ranges[0].base = screen_info.lfb_base;
+	ap->ranges[0].size = size_total;
+
+	ret = has_conflicting_framebuffer(ap, true);
+
+	kfree(ap);
+
+	return ret;
+}
+
 static int __init vesafb_probe(struct platform_device *dev)
 {
 	struct fb_info *info;
@@ -237,6 +256,9 @@  static int __init vesafb_probe(struct platform_device *dev)
 	if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB)
 		return -ENODEV;
 
+	if (aperture_already_claimed())
+		return -ENODEV;
+
 	vga_compat = (screen_info.capabilities & 2) ? 0 : 1;
 	vesafb_fix.smem_start = screen_info.lfb_base;
 	vesafb_defined.bits_per_pixel = screen_info.lfb_depth;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 58b9860..7dfd964 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -611,6 +611,8 @@  extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
 extern int register_framebuffer(struct fb_info *fb_info);
 extern int unregister_framebuffer(struct fb_info *fb_info);
 extern int unlink_framebuffer(struct fb_info *fb_info);
+extern bool has_conflicting_framebuffer(struct apertures_struct *a,
+					bool primary);
 extern void remove_conflicting_framebuffers(struct apertures_struct *a,
 				const char *name, bool primary);
 extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);