efi: Support EDID information

In the EFI config table, rename LINUX_EFI_SCREEN_INFO_TABLE_GUID to
LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID. Read sysfb_primary_display from
the entry. In addition to the screen_info, the entry now also contains
EDID information.

In libstub, replace struct screen_info with struct sysfb_display_info
from the kernel's sysfb_primary_display and rename functions
accordingly.  Transfer it to the runtime kernel using the kernel's
global state or the LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID config-table
entry.

With CONFIG_FIRMWARE_EDID=y, libstub now transfers the GOP device's EDID
information to the kernel. If CONFIG_FIRMWARE_EDID=n, EDID information
is disabled. Make the Kconfig symbol CONFIG_FIRMWARE_EDID available with
EFI. Setting the value to 'n' disables EDID support.

Also rename screen_info.c to primary_display.c and adapt the contained
comment according to the changes.

Link: https://lore.kernel.org/all/20251126160854.553077-8-tzimmermann@suse.de/
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
[ardb: depend on EFI_GENERIC_STUB not EFI, fix conflicts after dropping
       the preceding patch from the series]
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Thomas Zimmermann 2025-11-26 17:03:25 +01:00 committed by Ard Biesheuvel
parent 4fcae63588
commit c5a8f13f1e
12 changed files with 124 additions and 109 deletions

View file

@ -72,7 +72,7 @@ bool efi_poweroff_required(void)
(acpi_gbl_reduced_hardware || acpi_no_s5);
}
unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
unsigned long __initdata primary_display_table = EFI_INVALID_TABLE_ADDR;
#if defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)
struct sysfb_display_info sysfb_primary_display __section(".data");
@ -81,19 +81,19 @@ EXPORT_SYMBOL_GPL(sysfb_primary_display);
static void __init init_primary_display(void)
{
struct screen_info *si;
struct sysfb_display_info *dpy;
if (screen_info_table == EFI_INVALID_TABLE_ADDR)
if (primary_display_table == EFI_INVALID_TABLE_ADDR)
return;
si = early_memremap(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
dpy = early_memremap(primary_display_table, sizeof(*dpy));
if (!dpy) {
pr_err("Could not map primary_display config table\n");
return;
}
sysfb_primary_display.screen = *si;
memset(si, 0, sizeof(*si));
early_memunmap(si, sizeof(*si));
sysfb_primary_display = *dpy;
memset(dpy, 0, sizeof(*dpy));
early_memunmap(dpy, sizeof(*dpy));
memblock_reserve(__screen_info_lfb_base(&sysfb_primary_display.screen),
sysfb_primary_display.screen.lfb_size);

View file

@ -23,7 +23,7 @@
#include <asm/efi.h>
unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
unsigned long __initdata primary_display_table = EFI_INVALID_TABLE_ADDR;
static int __init is_memory(efi_memory_desc_t *md)
{
@ -67,17 +67,17 @@ EXPORT_SYMBOL_GPL(sysfb_primary_display);
static void __init init_primary_display(void)
{
struct screen_info *si;
struct sysfb_display_info *dpy;
if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
si = early_memremap(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
if (primary_display_table != EFI_INVALID_TABLE_ADDR) {
dpy = early_memremap(primary_display_table, sizeof(*dpy));
if (!dpy) {
pr_err("Could not map primary_display config table\n");
return;
}
sysfb_primary_display.screen = *si;
memset(si, 0, sizeof(*si));
early_memunmap(si, sizeof(*si));
sysfb_primary_display = *dpy;
memset(dpy, 0, sizeof(*dpy));
early_memunmap(dpy, sizeof(*dpy));
if (memblock_is_map_memory(sysfb_primary_display.screen.lfb_base))
memblock_mark_nomap(sysfb_primary_display.screen.lfb_base,

View file

@ -63,7 +63,7 @@ static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR;
extern unsigned long screen_info_table;
extern unsigned long primary_display_table;
struct mm_struct efi_mm = {
.mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock),
@ -641,7 +641,7 @@ static const efi_config_table_type_t common_tables[] __initconst = {
{LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" },
#endif
#ifdef CONFIG_EFI_GENERIC_STUB
{LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table },
{LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID, &primary_display_table },
#endif
{},
};

View file

@ -80,7 +80,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c)
lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
screen_info.o efi-stub-entry.o
primary_display.o efi-stub-entry.o
lib-$(CONFIG_ARM) += arm32-stub.o
lib-$(CONFIG_ARM64) += kaslr.o arm64.o arm64-stub.o smbios.o

View file

@ -14,18 +14,15 @@ static void *kernel_image_addr(void *addr)
return addr + kernel_image_offset;
}
struct screen_info *alloc_screen_info(void)
struct sysfb_display_info *alloc_primary_display(void)
{
if (IS_ENABLED(CONFIG_ARM))
return __alloc_screen_info();
return __alloc_primary_display();
if (IS_ENABLED(CONFIG_X86) ||
IS_ENABLED(CONFIG_EFI_EARLYCON) ||
IS_ENABLED(CONFIG_SYSFB)) {
struct sysfb_display_info *dpy = kernel_image_addr(&sysfb_primary_display);
return &dpy->screen;
}
IS_ENABLED(CONFIG_SYSFB))
return kernel_image_addr(&sysfb_primary_display);
return NULL;
}

View file

@ -10,7 +10,7 @@
*/
#include <linux/efi.h>
#include <linux/screen_info.h>
#include <linux/sysfb.h>
#include <asm/efi.h>
#include "efistub.h"
@ -48,23 +48,33 @@
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
void __weak free_screen_info(struct screen_info *si)
void __weak free_primary_display(struct sysfb_display_info *dpy)
{ }
static struct sysfb_display_info *setup_primary_display(void)
{
}
struct sysfb_display_info *dpy;
struct screen_info *screen = NULL;
struct edid_info *edid = NULL;
efi_status_t status;
static struct screen_info *setup_graphics(void)
{
struct screen_info *si, tmp = {};
if (efi_setup_graphics(&tmp, NULL) != EFI_SUCCESS)
dpy = alloc_primary_display();
if (!dpy)
return NULL;
screen = &dpy->screen;
#if defined(CONFIG_FIRMWARE_EDID)
edid = &dpy->edid;
#endif
si = alloc_screen_info();
if (!si)
return NULL;
status = efi_setup_graphics(screen, edid);
if (status != EFI_SUCCESS)
goto err_free_primary_display;
*si = tmp;
return si;
return dpy;
err_free_primary_display:
free_primary_display(dpy);
return NULL;
}
static void install_memreserve_table(void)
@ -145,14 +155,14 @@ efi_status_t efi_stub_common(efi_handle_t handle,
unsigned long image_addr,
char *cmdline_ptr)
{
struct screen_info *si;
struct sysfb_display_info *dpy;
efi_status_t status;
status = check_platform_features();
if (status != EFI_SUCCESS)
return status;
si = setup_graphics();
dpy = setup_primary_display();
efi_retrieve_eventlog();
@ -172,7 +182,8 @@ efi_status_t efi_stub_common(efi_handle_t handle,
status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr);
free_screen_info(si);
free_primary_display(dpy);
return status;
}

View file

@ -36,6 +36,7 @@
struct edid_info;
struct screen_info;
struct sysfb_display_info;
extern bool efi_no5lvl;
extern bool efi_nochunk;
@ -1175,9 +1176,9 @@ efi_enable_reset_attack_mitigation(void) { }
void efi_retrieve_eventlog(void);
struct screen_info *alloc_screen_info(void);
struct screen_info *__alloc_screen_info(void);
void free_screen_info(struct screen_info *si);
struct sysfb_display_info *alloc_primary_display(void);
struct sysfb_display_info *__alloc_primary_display(void);
void free_primary_display(struct sysfb_display_info *dpy);
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size);

View file

@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <linux/sysfb.h>
#include <asm/efi.h>
#include "efistub.h"
/*
* There are two ways of populating the core kernel's sysfb_primary_display
* via the stub:
*
* - using a configuration table, which relies on the EFI init code to
* locate the table and copy the contents; or
*
* - by linking directly to the core kernel's copy of the global symbol.
*
* The latter is preferred because it makes the EFIFB earlycon available very
* early, but it only works if the EFI stub is part of the core kernel image
* itself. The zboot decompressor can only use the configuration table
* approach.
*/
static efi_guid_t primary_display_guid = LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID;
struct sysfb_display_info *__alloc_primary_display(void)
{
struct sysfb_display_info *dpy;
efi_status_t status;
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
sizeof(*dpy), (void **)&dpy);
if (status != EFI_SUCCESS)
return NULL;
memset(dpy, 0, sizeof(*dpy));
status = efi_bs_call(install_configuration_table,
&primary_display_guid, dpy);
if (status == EFI_SUCCESS)
return dpy;
efi_bs_call(free_pool, dpy);
return NULL;
}
void free_primary_display(struct sysfb_display_info *dpy)
{
if (!dpy)
return;
efi_bs_call(install_configuration_table, &primary_display_guid, NULL);
efi_bs_call(free_pool, dpy);
}

View file

@ -1,53 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <linux/screen_info.h>
#include <asm/efi.h>
#include "efistub.h"
/*
* There are two ways of populating the core kernel's struct screen_info via the stub:
* - using a configuration table, like below, which relies on the EFI init code
* to locate the table and copy the contents;
* - by linking directly to the core kernel's copy of the global symbol.
*
* The latter is preferred because it makes the EFIFB earlycon available very
* early, but it only works if the EFI stub is part of the core kernel image
* itself. The zboot decompressor can only use the configuration table
* approach.
*/
static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID;
struct screen_info *__alloc_screen_info(void)
{
struct screen_info *si;
efi_status_t status;
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
sizeof(*si), (void **)&si);
if (status != EFI_SUCCESS)
return NULL;
memset(si, 0, sizeof(*si));
status = efi_bs_call(install_configuration_table,
&screen_info_guid, si);
if (status == EFI_SUCCESS)
return si;
efi_bs_call(free_pool, si);
return NULL;
}
void free_screen_info(struct screen_info *si)
{
if (!si)
return;
efi_bs_call(install_configuration_table, &screen_info_guid, NULL);
efi_bs_call(free_pool, si);
}

View file

@ -26,9 +26,9 @@ void __weak efi_cache_sync_image(unsigned long image_base,
// executable code loaded into memory to be safe for execution.
}
struct screen_info *alloc_screen_info(void)
struct sysfb_display_info *alloc_primary_display(void)
{
return __alloc_screen_info();
return __alloc_primary_display();
}
asmlinkage efi_status_t __efiapi

View file

@ -63,11 +63,13 @@ endif # HAS_IOMEM
config FIRMWARE_EDID
bool "Enable firmware EDID"
depends on X86
depends on EFI_GENERIC_STUB || X86
help
This enables access to the EDID transferred from the firmware.
On x86, this is from the VESA BIOS. DRM display drivers will
be able to export the information to userspace.
On EFI systems, the EDID comes from the same device as the
primary GOP. On x86 with BIOS, it comes from the VESA BIOS.
DRM display drivers will be able to export the information
to userspace.
Also enable this if DDC/I2C transfers do not work for your driver
and if you are using nvidiafb, i810fb or savagefb.

View file

@ -406,11 +406,12 @@ void efi_native_runtime_setup(void);
#define EFI_CC_FINAL_EVENTS_TABLE_GUID EFI_GUID(0xdd4a4648, 0x2de7, 0x4665, 0x96, 0x4d, 0x21, 0xd9, 0xef, 0x5f, 0xb4, 0x46)
/*
* This GUID is used to pass to the kernel proper the struct screen_info
* structure that was populated by the stub based on the GOP protocol instance
* associated with ConOut
* This GUIDs are used to pass to the kernel proper the primary
* display that has been populated by the stub based on the GOP
* instance associated with ConOut.
*/
#define LINUX_EFI_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
#define LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989, 0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
#define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
#define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)