From: David Herrmann <dh.herrmann@gmail.com>
To: dri-devel@lists.freedesktop.org
Subject: [PATCH v5 4/7] video: add generic framebuffer eviction
Date: Fri, 2 Sep 2016 10:22:42 +0200 [thread overview]
Message-ID: <20160902082245.7119-5-dh.herrmann@gmail.com> (raw)
In-Reply-To: <20160902082245.7119-1-dh.herrmann@gmail.com>
There are several situations where we want hardware handover from an early
boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched
GFX driver (e.g., most DRM drivers). So far, we relied on
remove_conflicting_framebuffers() to do this for us, however, this had a
bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon,
early boot console drivers, conflicting DRM drivers, etc.
o It only unloads the fbdev driver, it does not modify the underlying
device or resources. In case of "screen_info" drivers (e.g., efifb)
this is fine, since no resources are pinned. However, if the driver
binds to a platform-device like "simple-framebuffer", we must make
sure to unregister that device as well. Otherwise, pinned resources
like IORESOURCE_MEM stay around, triggering WARN_ONs if the following
driver requests those resources.
o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers
(short: sysfb). The initial commit provides conflict-resolution for
system-framebuffers. At its core it provides sysfb_evict_conflicts(),
which implements conflict detection and removal for all known types of
GFX driver hand-overs. So far, this includes platform-device removal,
fbdev-firmware-device removal, vgacon removal and VBE detection. To
further simplify the callers, it also provides helpers to figure out what
hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure out
the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in
memory can use sysfb_evict_conflicts_firmware(), basically removing
*all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/video/Kconfig | 4 +
drivers/video/Makefile | 1 +
drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/sysfb.h | 34 +++++
4 files changed, 366 insertions(+)
create mode 100644 drivers/video/sysfb.c
create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3c20af9..56a8294 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS
config HDMI
bool
+config SYSFB
+ bool
+ select DUMMY_CONSOLE if VT
+
if VT
source "drivers/video/console/Kconfig"
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9ad3c17..df7bd75 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
+obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c
new file mode 100644
index 0000000..00585c9
--- /dev/null
+++ b/drivers/video/sysfb.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) "sysfb: " fmt
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fb.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfb.h>
+#include <linux/vt.h>
+
+static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx,
+ struct resource *mem)
+{
+ struct aperture *g;
+ unsigned int i;
+
+ for (i = 0; i < ctx->ap->count; ++i) {
+ g = &ctx->ap->ranges[i];
+
+ if (mem->start == g->base)
+ return true;
+ if (mem->start >= g->base && mem->end < g->base + g->size)
+ return true;
+ if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start == 0xA0000)
+ return true;
+ }
+
+ return false;
+}
+
+static int sysfb_evict_platform_device(struct device *dev, void *userdata)
+{
+ struct sysfb_evict_ctx *ctx = userdata;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *mem;
+
+ if (!pdev->name)
+ return 0;
+
+ if (!strcmp(pdev->name, "simple-framebuffer")) {
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return 0;
+ if (!sysfb_evict_match_resource(ctx, mem))
+ return 0;
+
+#ifdef CONFIG_OF_ADDRESS
+ if (dev->of_node)
+ of_platform_device_destroy(dev);
+ else
+#endif
+ if (dev_get_platdata(&pdev->dev))
+ platform_device_del(pdev);
+ }
+
+ return 0;
+}
+
+static int sysfb_evict_platform(struct sysfb_evict_ctx *ctx)
+{
+ /*
+ * Early-boot architecture setup and boot-loarders sometimes create
+ * preliminary platform devices with a generic framebuffer setup. This
+ * allows graphics access during boot-up, without a real graphics
+ * driver loaded. However, once a real graphics driver takes over, we
+ * have to destroy those platform devices. In the legacy fbdev case, we
+ * just used to unload the fbdev driver. However, to make sure any kind
+ * of driver is unloaded, the platform-eviction code here simply
+ * removes any conflicting platform device directly. This causes any
+ * bound driver to be detached, and then removes the device entirely so
+ * it cannot be bound to later on.
+ *
+ * Please note that any such platform device must be registered by
+ * early architecture setup code. If they are registered after regular
+ * GFX drivers, this will fail horribly.
+ */
+
+ static DEFINE_MUTEX(lock);
+ int ret;
+
+ /*
+ * In case of static platform-devices, we must iterate the bus and
+ * remove them manually. We know that we're the only code that might
+ * remove them, so a simple static lock serializes all calls here.
+ */
+ mutex_lock(&lock);
+ ret = bus_for_each_dev(&platform_bus_type, NULL, ctx,
+ sysfb_evict_platform_device);
+ mutex_unlock(&lock);
+ return ret;
+}
+
+static int sysfb_evict_fbdev(struct sysfb_evict_ctx *ctx)
+{
+ /*
+ * Usually, evicting platform devices should be enough to also trigger
+ * fbdev unloading. However, some legacy devices (e.g., uvesafb) have
+ * no platform devices that can be evicted, so we still fall back to
+ * the legacy fbdev removal code. Note that this only removes fbdev
+ * devices marked as FBINFO_MISC_FIRMWARE. Anything else is left
+ * untouched.
+ *
+ * As usual, this only works if the fbdev device is probed early,
+ * before any real GFX driver wants to take over.
+ */
+
+ int ret = 0;
+
+#ifdef CONFIG_FB
+ ret = remove_conflicting_framebuffers(ctx->ap, "sysfb",
+ ctx->flags & SYSFB_EVICT_VBE);
+#endif
+
+ return ret;
+}
+
+static int sysfb_evict_vgacon(struct sysfb_evict_ctx *ctx)
+{
+ /*
+ * The VGACON console driver pokes at VGA registers randomly. If a GFX
+ * driver cannot keep the VGA support alive, it better makes sure to
+ * unload VGACON before probing.
+ *
+ * Unloading VGACON requires us to first force dummycon to take over
+ * from vgacon (but only if vgacon is really in use), followed by a
+ * deregistration of vgacon. Note that this prevents vgacon from being
+ * used again after the GFX driver is unloaded. But that is usually
+ * fine, since VGA state is rarely restored on driver-unload, anyway.
+ *
+ * Note that we rely on VGACON to be probed in early boot (actually
+ * done by ARCH setup code). If it is probed after GFX drivers, this
+ * will fail horribly. You better make sure VGACON is probed early and
+ * GFX drivers are probed as normal modules.
+ */
+
+ int ret = 0;
+
+#ifdef CONFIG_VGA_CONSOLE
+ console_lock();
+ if (con_is_bound(&vga_con))
+ ret = do_take_over_console(&dummy_con, 0,
+ MAX_NR_CONSOLES - 1, 1);
+ if (ret == 0) {
+ ret = do_unregister_con_driver(&vga_con);
+ if (ret == -ENODEV) /* ignore "already unregistered" */
+ ret = 0;
+ }
+ console_unlock();
+#endif
+
+ return ret;
+}
+
+/**
+ * sysfb_evict_conflicts - remove any conflicting system-framebuffers
+ * @ctx: eviction context
+ *
+ * This function evicts any conflicting system-framebuffers and their bound
+ * drivers, according to the data given in @ctx.
+ *
+ * Depending on @ctx->flags, the following operations are performed:
+ *
+ * SYSFB_EVICT_PLATFORM: Firmware framebuffer platform devices (eg.,
+ * 'simple-framebuffer') that overlap @ctx are removed
+ * from the system, causing drivers to be unbound.
+ * If SYSFB_EVICT_VBE is given, this also affects
+ * devices that own the VGA region.
+ *
+ * SYSFB_EVICT_FBDEV: Any firmware fbdev drivers that overlap @ctx are
+ * unloaded.
+ * Furthermore, if SYSFB_EVICT_VBE is given as well, any
+ * fbdev driver that maps the VGA region is unloaded.
+ *
+ * SYSFB_EVICT_VGACON: The vgacon console driver is unbound and unregistered.
+ *
+ * This might call into fbdev driver unregistration, or even device_del() on
+ * some buses. Hence, make sure you call this from your top-level
+ * probe-callbacks, rather than with any gfx-subsystem locks held.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx)
+{
+ int ret;
+
+ if (WARN_ON(!ctx || !ctx->ap))
+ return -EINVAL;
+
+ pr_info("removing conflicts (sysfb%s%s%s%s)\n",
+ (ctx->flags & SYSFB_EVICT_PLATFORM) ? ", platform" : "",
+ (ctx->flags & SYSFB_EVICT_FBDEV) ? ", fbdev" : "",
+ (ctx->flags & SYSFB_EVICT_VGACON) ? ", vgacon" : "",
+ (ctx->flags & SYSFB_EVICT_VBE) ? ", vbe" : "");
+
+ if (ctx->flags & SYSFB_EVICT_PLATFORM) {
+ ret = sysfb_evict_platform(ctx);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ctx->flags & SYSFB_EVICT_FBDEV) {
+ ret = sysfb_evict_fbdev(ctx);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ctx->flags & SYSFB_EVICT_VGACON) {
+ ret = sysfb_evict_vgacon(ctx);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sysfb_evict_conflicts);
+
+/**
+ * sysfb_evict_conflicts_firmware() - remove all firmware framebuffers
+ *
+ * This is similar to sysfb_evict_conflicts() but uses a fake aperture spanning
+ * the entire address-space. This is suitable for any GFX driver that just
+ * wants to get rid of all available firmware framebuffers.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int sysfb_evict_conflicts_firmware(void)
+{
+ struct sysfb_evict_ctx ctx = {};
+ int ret;
+
+ ctx.ap = alloc_apertures(1);
+ if (!ctx.ap)
+ return -ENOMEM;
+
+ ctx.ap->ranges[0].base = 0;
+ ctx.ap->ranges[0].size = ~0;
+
+ ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
+ ret = sysfb_evict_conflicts(&ctx);
+
+ kfree(ctx.ap);
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_evict_conflicts_firmware);
+
+#ifdef CONFIG_PCI
+/**
+ * sysfb_evict_conflicts_pci() - remove all system framebuffers conflicting
+ * with the given pci device
+ * @pdev: pci device
+ *
+ * This is similar to sysfb_evict_conflicts() but generates the eviction
+ * context based on the given pci device @pdev.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int sysfb_evict_conflicts_pci(struct pci_dev *pdev)
+{
+ struct sysfb_evict_ctx ctx = {};
+ size_t i, n, offset;
+ int ret;
+
+ /*
+ * If this device is used as primary VGA device, it is shadowed at the
+ * VBE base address, so make sure to include it in the apertures.
+ */
+ if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
+ ctx.flags |= SYSFB_EVICT_VBE;
+
+ /*
+ * If a device is a VGA device, make sure to kick out vgacon. We cannot
+ * rely on the IORESOURCE_ROM_SHADOW, since vgacon can switch between
+ * vga devices at runtime. So kick out vgacon anyway.
+ */
+ if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+ ctx.flags |= SYSFB_EVICT_VGACON;
+
+ /*
+ * Allocate apertures for all standard PCI resources. Skip them in case
+ * they are empty.
+ */
+ ctx.ap = alloc_apertures(PCI_STD_RESOURCE_END - PCI_STD_RESOURCES + 1);
+ if (!ctx.ap)
+ return -ENOMEM;
+
+ offset = PCI_STD_RESOURCES;
+ for (n = 0, i = 0; i < ctx.ap->count; ++i) {
+ if (pci_resource_len(pdev, offset + i) < 1)
+ continue;
+
+ ctx.ap->ranges[n].base = pci_resource_start(pdev, offset + i);
+ ctx.ap->ranges[n].size = pci_resource_len(pdev, offset + i);
+ ++n;
+ }
+ ctx.ap->count = n;
+
+ /*
+ * Evict all matching fbdev devices, VBE devices if they shadow this
+ * device, vgacon if this is a vga device, and platform devices if they
+ * match.
+ */
+ ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
+ ret = sysfb_evict_conflicts(&ctx);
+
+ kfree(ctx.ap);
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_evict_conflicts_pci);
+#endif /* CONFIG_PCI */
diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
new file mode 100644
index 0000000..b67c74a
--- /dev/null
+++ b/include/linux/sysfb.h
@@ -0,0 +1,34 @@
+#ifndef __LINUX_SYSFB_H
+#define __LINUX_SYSFB_H
+
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+
+struct apertures_struct;
+struct pci_dev;
+
+enum {
+ SYSFB_EVICT_PLATFORM = (1U << 0),
+ SYSFB_EVICT_FBDEV = (1U << 1),
+ SYSFB_EVICT_VGACON = (1U << 2),
+ SYSFB_EVICT_VBE = (1U << 3),
+};
+
+struct sysfb_evict_ctx {
+ struct apertures_struct *ap;
+ unsigned int flags;
+};
+
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx);
+int sysfb_evict_conflicts_firmware(void);
+int sysfb_evict_conflicts_pci(struct pci_dev *pdev);
+
+#endif /* __LINUX_SYSFB_H */
--
2.9.3
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
next prev parent reply other threads:[~2016-09-02 8:23 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-02 8:22 [PATCH v5 0/7] drm: add simpledrm driver David Herrmann
2016-09-02 8:22 ` [PATCH v5 1/7] x86/sysfb: add support for 64bit EFI lfb_base David Herrmann
2016-09-02 10:20 ` Tom Gundersen
2016-09-02 8:22 ` [PATCH v5 2/7] x86/sysfb: fix lfb_size calculation David Herrmann
2016-09-02 10:20 ` Tom Gundersen
2016-09-02 8:22 ` [PATCH v5 3/7] of/platform: expose of_platform_device_destroy() David Herrmann
2016-09-02 10:21 ` Tom Gundersen
2016-09-02 8:22 ` David Herrmann [this message]
2016-09-02 10:21 ` [PATCH v5 4/7] video: add generic framebuffer eviction Tom Gundersen
2016-09-03 12:06 ` Noralf Trønnes
2016-09-05 11:19 ` David Herrmann
2016-09-05 16:36 ` Noralf Trønnes
2016-09-02 8:22 ` [PATCH v5 5/7] drm: switch to sysfb_evict_conflicts() David Herrmann
2016-09-03 12:13 ` Noralf Trønnes
2016-09-02 8:22 ` [PATCH v5 6/7] drm: add SimpleDRM driver David Herrmann
2016-09-02 12:45 ` Tom Gundersen
2016-09-03 12:01 ` Noralf Trønnes
2016-09-03 12:05 ` David Herrmann
2016-09-05 16:39 ` Noralf Trønnes
2016-09-02 8:22 ` [PATCH v5 7/7] drm/simpledrm: add fbdev fallback support David Herrmann
2016-09-03 12:04 ` Noralf Trønnes
2016-09-03 17:15 ` Noralf Trønnes
2016-09-05 11:21 ` David Herrmann
2021-03-10 2:50 ` [PATCH v5 0/7] drm: add simpledrm driver nerdopolis
2021-03-10 9:10 ` Thomas Zimmermann
2021-03-10 13:52 ` nerdopolis
2021-03-12 3:49 ` nerdopolis
2021-03-12 8:03 ` Thomas Zimmermann
2021-03-12 13:25 ` nerdopolis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160902082245.7119-5-dh.herrmann@gmail.com \
--to=dh.herrmann@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.