efi: libstub: Move screen_info handling to common code
authorArd Biesheuvel <ardb@kernel.org>
Tue, 11 Oct 2022 15:10:39 +0000 (17:10 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Wed, 9 Nov 2022 11:42:02 +0000 (12:42 +0100)
Currently, arm64, RISC-V and LoongArch rely on the fact that struct
screen_info can be accessed directly, due to the fact that the EFI stub
and the core kernel are part of the same image. This will change after a
future patch, so let's ensure that the screen_info handling is able to
deal with this, by adopting the arm32 approach of passing it as a
configuration table. While at it, switch to ACPI reclaim memory to hold
the screen_info data, which is more appropriate for this kind of
allocation.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
14 files changed:
arch/arm/include/asm/efi.h
arch/arm/kernel/efi.c
arch/arm64/include/asm/efi.h
arch/loongarch/include/asm/efi.h
arch/loongarch/kernel/efi.c
arch/riscv/include/asm/efi.h
drivers/firmware/efi/efi-init.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/arm32-stub.c
drivers/firmware/efi/libstub/efi-stub.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/screen_info.c [new file with mode: 0644]
include/linux/efi.h

index 4bdd930167c0f18c2d7dba832b0315a91264ff79..b95241b1ca656f3c9152825cdb5549742753494c 100644 (file)
@@ -43,9 +43,6 @@ void efi_virtmap_unload(void);
 
 /* arch specific definitions used by the stub code */
 
-struct screen_info *alloc_screen_info(void);
-void free_screen_info(struct screen_info *si);
-
 /*
  * 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
index e50ad7eefc02a6eb08b2736644f4724fa3aad2ee..882104f43b3b09281e406d2ed8d7244726b32980 100644 (file)
@@ -75,38 +75,13 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
        return 0;
 }
 
-static unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
 static unsigned long __initdata cpu_state_table = EFI_INVALID_TABLE_ADDR;
 
 const efi_config_table_type_t efi_arch_tables[] __initconst = {
-       {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
        {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
        {}
 };
 
-static void __init load_screen_info_table(void)
-{
-       struct screen_info *si;
-
-       if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
-               si = early_memremap_ro(screen_info_table, sizeof(*si));
-               if (!si) {
-                       pr_err("Could not map screen_info config table\n");
-                       return;
-               }
-               screen_info = *si;
-               early_memunmap(si, sizeof(*si));
-
-               /* dummycon on ARM needs non-zero values for columns/lines */
-               screen_info.orig_video_cols = 80;
-               screen_info.orig_video_lines = 25;
-
-               if (memblock_is_map_memory(screen_info.lfb_base))
-                       memblock_mark_nomap(screen_info.lfb_base,
-                                           screen_info.lfb_size);
-       }
-}
-
 static void __init load_cpu_state_table(void)
 {
        if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
@@ -145,7 +120,11 @@ void __init arm_efi_init(void)
 {
        efi_init();
 
-       load_screen_info_table();
+       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
+               /* dummycon on ARM needs non-zero values for columns/lines */
+               screen_info.orig_video_cols = 80;
+               screen_info.orig_video_lines = 25;
+       }
 
        /* ARM does not permit early mappings to persist across paging_init() */
        efi_memmap_unmap();
index d6cf535d8352b32451ef90a85349f07330c62a5b..8604473a85b8b84f8952db3afcc9cd11e5c34a95 100644 (file)
@@ -84,12 +84,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
        return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define alloc_screen_info(x...)                &screen_info
-
-static inline void free_screen_info(struct screen_info *si)
-{
-}
-
 #define EFI_ALLOC_ALIGN                SZ_64K
 
 /*
index 174567b00ddb907d32241ae45a7d71ff2e902050..60d6a170c18d9a07a25a9cc9c39c44a975c8066a 100644 (file)
@@ -19,15 +19,6 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
 #define EFI_ALLOC_ALIGN                SZ_64K
 #define EFI_RT_VIRTUAL_OFFSET  CSR_DMW0_BASE
 
-static inline struct screen_info *alloc_screen_info(void)
-{
-       return &screen_info;
-}
-
-static inline void free_screen_info(struct screen_info *si)
-{
-}
-
 static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
 {
        return ULONG_MAX;
index a31329971133946d383fc16fc2eaf99d78f0bcbb..d75ce73e8ff82b77a3491aeb15a772740084b085 100644 (file)
@@ -52,6 +52,27 @@ void __init efi_runtime_init(void)
        set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 }
 
+unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
+
+static void __init init_screen_info(void)
+{
+       struct screen_info *si;
+
+       if (screen_info_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");
+               return;
+       }
+       screen_info = *si;
+       memset(si, 0, sizeof(*si));
+       early_memunmap(si, sizeof(*si));
+
+       memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
+}
+
 void __init efi_init(void)
 {
        int size;
@@ -80,8 +101,7 @@ void __init efi_init(void)
 
        set_bit(EFI_CONFIG_TABLES, &efi.flags);
 
-       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
-               memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
+       init_screen_info();
 
        if (boot_memmap == EFI_INVALID_TABLE_ADDR)
                return;
index f74879a8f1ea17bc3476d5559b6a43b43241d2ea..d0570936cb8c0bc1ecd1a52a2eef651833a05c41 100644 (file)
@@ -31,12 +31,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
        return ULONG_MAX;
 }
 
-#define alloc_screen_info(x...)                (&screen_info)
-
-static inline void free_screen_info(struct screen_info *si)
-{
-}
-
 void efi_virtmap_load(void);
 void efi_virtmap_unload(void);
 
index 2fd770b499a35301f2c0c04f09cd379fa11dcf30..1639159493e3e9992a5178e492fdd628da2b9a9b 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <asm/efi.h>
 
+unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
+
 static int __init is_memory(efi_memory_desc_t *md)
 {
        if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
@@ -55,9 +57,22 @@ extern __weak const efi_config_table_type_t efi_arch_tables[];
 
 static void __init init_screen_info(void)
 {
-       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
-           memblock_is_map_memory(screen_info.lfb_base))
-               memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
+       struct screen_info *si;
+
+       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");
+                       return;
+               }
+               screen_info = *si;
+               memset(si, 0, sizeof(*si));
+               early_memunmap(si, sizeof(*si));
+
+               if (memblock_is_map_memory(screen_info.lfb_base))
+                       memblock_mark_nomap(screen_info.lfb_base,
+                                           screen_info.lfb_size);
+       }
 }
 
 static int __init uefi_init(u64 efi_system_table)
index a46df5d1d0942751635276f6f1dc2710305ffae1..951a42d27cf431cc5f639103c0dc69e94f065300 100644 (file)
@@ -58,6 +58,8 @@ 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;
+
 struct mm_struct efi_mm = {
        .mm_mt                  = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock),
        .mm_users               = ATOMIC_INIT(2),
@@ -546,6 +548,9 @@ static const efi_config_table_type_t common_tables[] __initconst = {
 #endif
 #ifdef CONFIG_EFI_COCO_SECRET
        {LINUX_EFI_COCO_SECRET_AREA_GUID,       &efi.coco_secret,       "CocoSecret"    },
+#endif
+#ifdef CONFIG_EFI_GENERIC_STUB
+       {LINUX_EFI_SCREEN_INFO_TABLE_GUID,      &screen_info_table                      },
 #endif
        {},
 };
index 5b0ae755180e227bec429e6bf41b8a874a5b61ff..3f44e272ff9c791b1b29a70fe8e6f73bbbd17f80 100644 (file)
@@ -81,7 +81,8 @@ lib-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdt.o \
 $(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
+lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
+                                  screen_info.o
 
 lib-$(CONFIG_ARM)              += arm32-stub.o
 lib-$(CONFIG_ARM64)            += arm64-stub.o arm64-entry.o
index 0131e3aaa6055317993267ca2d354c09cc95f310..1073dd947516026301e38d435639d02d7919deca 100644 (file)
@@ -76,43 +76,6 @@ void efi_handle_post_ebs_state(void)
                      &efi_entry_state->sctlr_after_ebs);
 }
 
-static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
-
-struct screen_info *alloc_screen_info(void)
-{
-       struct screen_info *si;
-       efi_status_t status;
-
-       /*
-        * Unlike on arm64, where we can directly fill out the screen_info
-        * structure from the stub, we need to allocate a buffer to hold
-        * its contents while we hand over to the kernel proper from the
-        * decompressor.
-        */
-       status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
-                            sizeof(*si), (void **)&si);
-
-       if (status != EFI_SUCCESS)
-               return NULL;
-
-       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);
-}
-
 efi_status_t handle_kernel_image(unsigned long *image_addr,
                                 unsigned long *image_size,
                                 unsigned long *reserve_addr,
index b55d1009d4c880effb2dde16a9932e0155756049..8521dc09c6ae44875dcf9292a55dd7d05b964224 100644 (file)
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
 static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
 
+struct screen_info * __weak alloc_screen_info(void)
+{
+       return &screen_info;
+}
+
+void __weak free_screen_info(struct screen_info *si)
+{
+}
+
 static struct screen_info *setup_graphics(void)
 {
        efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
index a30fb5d8ef05ae9c781c70737ffcba2bf7d03059..a4cb51e2cae326ac47ca19d597dca83e60ed5146 100644 (file)
@@ -975,4 +975,7 @@ efi_enable_reset_attack_mitigation(void) { }
 
 void efi_retrieve_tpm2_eventlog(void);
 
+struct screen_info *alloc_screen_info(void);
+void free_screen_info(struct screen_info *si);
+
 #endif
diff --git a/drivers/firmware/efi/libstub/screen_info.c b/drivers/firmware/efi/libstub/screen_info.c
new file mode 100644 (file)
index 0000000..8e76a8b
--- /dev/null
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/efi.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.
+ *
+ * In order to support both methods from the same build of the EFI stub
+ * library, provide this dummy global definition of struct screen_info. If it
+ * is required to satisfy a link dependency, it means we need to override the
+ * __weak alloc and free methods with the ones below, and those will be pulled
+ * in as well.
+ */
+struct screen_info screen_info;
+
+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;
+
+       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);
+}
index 929d559ad41d29c6027dd825343b38c69d986e46..dfa6cc8bbef9034c53aca6c14b3d777666ed53f4 100644 (file)
@@ -403,7 +403,7 @@ void efi_native_runtime_setup(void);
  * structure that was populated by the stub based on the GOP protocol instance
  * associated with ConOut
  */
-#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID   EFI_GUID(0xe03fc20a, 0x85dc, 0x406e,  0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
+#define LINUX_EFI_SCREEN_INFO_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)