Skip to content

Commit

Permalink
drivers/firmware: consolidate EFI framebuffer setup for all arches
Browse files Browse the repository at this point in the history
The register_gop_device() function registers an "efi-framebuffer" platform
device to match against the efifb driver, to have an early framebuffer for
EFI platforms.

But there is already support to do exactly the same by the Generic System
Framebuffers (sysfb) driver. This used to be only for X86 but it has been
moved to drivers/firmware and could be reused by other architectures.

Also, besides supporting registering an "efi-framebuffer", this driver can
register a "simple-framebuffer" allowing to use the siple{fb,drm} drivers
on non-X86 EFI platforms. For example, on aarch64 these drivers can only
be used with DT and doesn't have code to register a "simple-frambuffer"
platform device when booting with EFI.

For these reasons, let's remove the register_gop_device() duplicated code
and instead move the platform specific logic that's there to sysfb driver.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Borislav Petkov <bp@suse.de>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210625131359.1804394-1-javierm@redhat.com
  • Loading branch information
martinezjavier authored and Thomas Zimmermann committed Jul 21, 2021
1 parent d391c58 commit 8633ef8
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 144 deletions.
5 changes: 1 addition & 4 deletions arch/arm/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#ifdef CONFIG_EFI
void efi_init(void);
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);

int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
Expand Down Expand Up @@ -52,10 +53,6 @@ void efi_virtmap_unload(void);
struct screen_info *alloc_screen_info(void);
void free_screen_info(struct screen_info *si);

static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
{
}

/*
* A reasonable upper bound for the uncompressed kernel size is 32 MBytes,
* so we will reserve that amount of memory. We have no easy way to tell what
Expand Down
5 changes: 1 addition & 4 deletions arch/arm64/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#ifdef CONFIG_EFI
extern void efi_init(void);
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
#else
#define efi_init()
#endif
Expand Down Expand Up @@ -85,10 +86,6 @@ static inline void free_screen_info(struct screen_info *si)
{
}

static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
{
}

#define EFI_ALLOC_ALIGN SZ_64K

/*
Expand Down
5 changes: 1 addition & 4 deletions arch/riscv/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#ifdef CONFIG_EFI
extern void efi_init(void);
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
#else
#define efi_init()
#endif
Expand All @@ -39,10 +40,6 @@ static inline void free_screen_info(struct screen_info *si)
{
}

static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
{
}

void efi_virtmap_load(void);
void efi_virtmap_unload(void);

Expand Down
8 changes: 4 additions & 4 deletions drivers/firmware/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -254,20 +254,20 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
config SYSFB
bool
default y
depends on X86 || COMPILE_TEST
depends on X86 || ARM || ARM64 || RISCV || COMPILE_TEST

config X86_SYSFB
config SYSFB_SIMPLEFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
depends on SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
user-guidance and debugging. Historically, x86 used the VESA BIOS
Extensions and EFI-framebuffers for this, which are mostly limited
to x86.
to x86 BIOS or EFI systems.
This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
framebuffers so the new generic system-framebuffer drivers can be
used on x86. If the framebuffer is not compatible with the generic
used instead. If the framebuffer is not compatible with the generic
modes, it is advertised as fallback platform framebuffer so legacy
drivers like efifb, vesafb and uvesafb can pick it up.
If this option is not selected, all system framebuffers are always
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_X86_SYSFB) += sysfb_simplefb.o
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
Expand Down
90 changes: 0 additions & 90 deletions drivers/firmware/efi/efi-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,93 +275,3 @@ void __init efi_init(void)
}
#endif
}

static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
{
u64 fb_base = screen_info.lfb_base;

if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
fb_base |= (u64)(unsigned long)screen_info.ext_lfb_base << 32;

return fb_base >= range->cpu_addr &&
fb_base < (range->cpu_addr + range->size);
}

static struct device_node *find_pci_overlap_node(void)
{
struct device_node *np;

for_each_node_by_type(np, "pci") {
struct of_pci_range_parser parser;
struct of_pci_range range;
int err;

err = of_pci_range_parser_init(&parser, np);
if (err) {
pr_warn("of_pci_range_parser_init() failed: %d\n", err);
continue;
}

for_each_of_pci_range(&parser, &range)
if (efifb_overlaps_pci_range(&range))
return np;
}
return NULL;
}

/*
* If the efifb framebuffer is backed by a PCI graphics controller, we have
* to ensure that this relation is expressed using a device link when
* running in DT mode, or the probe order may be reversed, resulting in a
* resource reservation conflict on the memory window that the efifb
* framebuffer steals from the PCIe host bridge.
*/
static int efifb_add_links(struct fwnode_handle *fwnode)
{
struct device_node *sup_np;

sup_np = find_pci_overlap_node();

/*
* If there's no PCI graphics controller backing the efifb, we are
* done here.
*/
if (!sup_np)
return 0;

fwnode_link_add(fwnode, of_fwnode_handle(sup_np));
of_node_put(sup_np);

return 0;
}

static const struct fwnode_operations efifb_fwnode_ops = {
.add_links = efifb_add_links,
};

static struct fwnode_handle efifb_fwnode;

static int __init register_gop_device(void)
{
struct platform_device *pd;
int err;

if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return 0;

pd = platform_device_alloc("efi-framebuffer", 0);
if (!pd)
return -ENOMEM;

if (IS_ENABLED(CONFIG_PCI)) {
fwnode_init(&efifb_fwnode, &efifb_fwnode_ops);
pd->dev.fwnode = &efifb_fwnode;
}

err = platform_device_add_data(pd, &screen_info, sizeof(screen_info));
if (err)
return err;

return platform_device_add(pd);
}
subsys_initcall(register_gop_device);
76 changes: 74 additions & 2 deletions drivers/firmware/efi/sysfb_efi.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic System Framebuffers on x86
* Generic System Framebuffers
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
*
* EFI Quirks Copyright (c) 2006 Edgar Hucek <gimli@dark-green.com>
Expand All @@ -19,7 +19,9 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
#include <linux/sysfb.h>
#include <video/vga.h>
Expand Down Expand Up @@ -267,7 +269,72 @@ static const struct dmi_system_id efifb_dmi_swap_width_height[] __initconst = {
{},
};

__init void sysfb_apply_efi_quirks(void)
static bool efifb_overlaps_pci_range(const struct of_pci_range *range)
{
u64 fb_base = screen_info.lfb_base;

if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
fb_base |= (u64)(unsigned long)screen_info.ext_lfb_base << 32;

return fb_base >= range->cpu_addr &&
fb_base < (range->cpu_addr + range->size);
}

static struct device_node *find_pci_overlap_node(void)
{
struct device_node *np;

for_each_node_by_type(np, "pci") {
struct of_pci_range_parser parser;
struct of_pci_range range;
int err;

err = of_pci_range_parser_init(&parser, np);
if (err) {
pr_warn("of_pci_range_parser_init() failed: %d\n", err);
continue;
}

for_each_of_pci_range(&parser, &range)
if (efifb_overlaps_pci_range(&range))
return np;
}
return NULL;
}

/*
* If the efifb framebuffer is backed by a PCI graphics controller, we have
* to ensure that this relation is expressed using a device link when
* running in DT mode, or the probe order may be reversed, resulting in a
* resource reservation conflict on the memory window that the efifb
* framebuffer steals from the PCIe host bridge.
*/
static int efifb_add_links(struct fwnode_handle *fwnode)
{
struct device_node *sup_np;

sup_np = find_pci_overlap_node();

/*
* If there's no PCI graphics controller backing the efifb, we are
* done here.
*/
if (!sup_np)
return 0;

fwnode_link_add(fwnode, of_fwnode_handle(sup_np));
of_node_put(sup_np);

return 0;
}

static const struct fwnode_operations efifb_fwnode_ops = {
.add_links = efifb_add_links,
};

static struct fwnode_handle efifb_fwnode;

__init void sysfb_apply_efi_quirks(struct platform_device *pd)
{
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
!(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
Expand All @@ -281,4 +348,9 @@ __init void sysfb_apply_efi_quirks(void)
screen_info.lfb_height = temp;
screen_info.lfb_linelength = 4 * screen_info.lfb_width;
}

if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI && IS_ENABLED(CONFIG_PCI)) {
fwnode_init(&efifb_fwnode, &efifb_fwnode_ops);
pd->dev.fwnode = &efifb_fwnode;
}
}
35 changes: 24 additions & 11 deletions drivers/firmware/sysfb.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic System Framebuffers on x86
* Generic System Framebuffers
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
*/

/*
* Simple-Framebuffer support for x86 systems
* Simple-Framebuffer support
* Create a platform-device for any available boot framebuffer. The
* simple-framebuffer platform device is already available on DT systems, so
* this module parses the global "screen_info" object and creates a suitable
Expand All @@ -16,12 +16,12 @@
* to pick these devices up without messing with simple-framebuffer drivers.
* The global "screen_info" is still valid at all times.
*
* If CONFIG_X86_SYSFB is not selected, we never register "simple-framebuffer"
* If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer"
* platform devices, but only use legacy framebuffer devices for
* backwards compatibility.
*
* TODO: We set the dev_id field of all platform-devices to 0. This allows
* other x86 OF/DT parsers to create such devices, too. However, they must
* other OF/DT parsers to create such devices, too. However, they must
* start at offset 1 for this to work.
*/

Expand All @@ -43,12 +43,10 @@ static __init int sysfb_init(void)
bool compatible;
int ret;

sysfb_apply_efi_quirks();

/* try to create a simple-framebuffer device */
compatible = parse_mode(si, &mode);
compatible = sysfb_parse_mode(si, &mode);
if (compatible) {
ret = create_simplefb(si, &mode);
ret = sysfb_create_simplefb(si, &mode);
if (!ret)
return 0;
}
Expand All @@ -61,9 +59,24 @@ static __init int sysfb_init(void)
else
name = "platform-framebuffer";

pd = platform_device_register_resndata(NULL, name, 0,
NULL, 0, si, sizeof(*si));
return PTR_ERR_OR_ZERO(pd);
pd = platform_device_alloc(name, 0);
if (!pd)
return -ENOMEM;

sysfb_apply_efi_quirks(pd);

ret = platform_device_add_data(pd, si, sizeof(*si));
if (ret)
goto err;

ret = platform_device_add(pd);
if (ret)
goto err;

return 0;
err:
platform_device_put(pd);
return ret;
}

/* must execute after PCI subsystem for EFI quirks */
Expand Down
Loading

0 comments on commit 8633ef8

Please sign in to comment.