Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

efi: libstub: Move screen_info handling to common code

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>

+121 -94
-3
arch/arm/include/asm/efi.h
··· 43 43 44 44 /* arch specific definitions used by the stub code */ 45 45 46 - struct screen_info *alloc_screen_info(void); 47 - void free_screen_info(struct screen_info *si); 48 - 49 46 /* 50 47 * A reasonable upper bound for the uncompressed kernel size is 32 MBytes, 51 48 * so we will reserve that amount of memory. We have no easy way to tell what
+5 -26
arch/arm/kernel/efi.c
··· 75 75 return 0; 76 76 } 77 77 78 - static unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; 79 78 static unsigned long __initdata cpu_state_table = EFI_INVALID_TABLE_ADDR; 80 79 81 80 const efi_config_table_type_t efi_arch_tables[] __initconst = { 82 - {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table}, 83 81 {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table}, 84 82 {} 85 83 }; 86 - 87 - static void __init load_screen_info_table(void) 88 - { 89 - struct screen_info *si; 90 - 91 - if (screen_info_table != EFI_INVALID_TABLE_ADDR) { 92 - si = early_memremap_ro(screen_info_table, sizeof(*si)); 93 - if (!si) { 94 - pr_err("Could not map screen_info config table\n"); 95 - return; 96 - } 97 - screen_info = *si; 98 - early_memunmap(si, sizeof(*si)); 99 - 100 - /* dummycon on ARM needs non-zero values for columns/lines */ 101 - screen_info.orig_video_cols = 80; 102 - screen_info.orig_video_lines = 25; 103 - 104 - if (memblock_is_map_memory(screen_info.lfb_base)) 105 - memblock_mark_nomap(screen_info.lfb_base, 106 - screen_info.lfb_size); 107 - } 108 - } 109 84 110 85 static void __init load_cpu_state_table(void) 111 86 { ··· 120 145 { 121 146 efi_init(); 122 147 123 - load_screen_info_table(); 148 + if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) { 149 + /* dummycon on ARM needs non-zero values for columns/lines */ 150 + screen_info.orig_video_cols = 80; 151 + screen_info.orig_video_lines = 25; 152 + } 124 153 125 154 /* ARM does not permit early mappings to persist across paging_init() */ 126 155 efi_memmap_unmap();
-6
arch/arm64/include/asm/efi.h
··· 84 84 return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1)); 85 85 } 86 86 87 - #define alloc_screen_info(x...) &screen_info 88 - 89 - static inline void free_screen_info(struct screen_info *si) 90 - { 91 - } 92 - 93 87 #define EFI_ALLOC_ALIGN SZ_64K 94 88 95 89 /*
-9
arch/loongarch/include/asm/efi.h
··· 19 19 #define EFI_ALLOC_ALIGN SZ_64K 20 20 #define EFI_RT_VIRTUAL_OFFSET CSR_DMW0_BASE 21 21 22 - static inline struct screen_info *alloc_screen_info(void) 23 - { 24 - return &screen_info; 25 - } 26 - 27 - static inline void free_screen_info(struct screen_info *si) 28 - { 29 - } 30 - 31 22 static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) 32 23 { 33 24 return ULONG_MAX;
+22 -2
arch/loongarch/kernel/efi.c
··· 52 52 set_bit(EFI_RUNTIME_SERVICES, &efi.flags); 53 53 } 54 54 55 + unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; 56 + 57 + static void __init init_screen_info(void) 58 + { 59 + struct screen_info *si; 60 + 61 + if (screen_info_table == EFI_INVALID_TABLE_ADDR) 62 + return; 63 + 64 + si = early_memremap(screen_info_table, sizeof(*si)); 65 + if (!si) { 66 + pr_err("Could not map screen_info config table\n"); 67 + return; 68 + } 69 + screen_info = *si; 70 + memset(si, 0, sizeof(*si)); 71 + early_memunmap(si, sizeof(*si)); 72 + 73 + memblock_reserve(screen_info.lfb_base, screen_info.lfb_size); 74 + } 75 + 55 76 void __init efi_init(void) 56 77 { 57 78 int size; ··· 101 80 102 81 set_bit(EFI_CONFIG_TABLES, &efi.flags); 103 82 104 - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) 105 - memblock_reserve(screen_info.lfb_base, screen_info.lfb_size); 83 + init_screen_info(); 106 84 107 85 if (boot_memmap == EFI_INVALID_TABLE_ADDR) 108 86 return;
-6
arch/riscv/include/asm/efi.h
··· 31 31 return ULONG_MAX; 32 32 } 33 33 34 - #define alloc_screen_info(x...) (&screen_info) 35 - 36 - static inline void free_screen_info(struct screen_info *si) 37 - { 38 - } 39 - 40 34 void efi_virtmap_load(void); 41 35 void efi_virtmap_unload(void); 42 36
+18 -3
drivers/firmware/efi/efi-init.c
··· 22 22 23 23 #include <asm/efi.h> 24 24 25 + unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; 26 + 25 27 static int __init is_memory(efi_memory_desc_t *md) 26 28 { 27 29 if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC)) ··· 57 55 58 56 static void __init init_screen_info(void) 59 57 { 60 - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI && 61 - memblock_is_map_memory(screen_info.lfb_base)) 62 - memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size); 58 + struct screen_info *si; 59 + 60 + if (screen_info_table != EFI_INVALID_TABLE_ADDR) { 61 + si = early_memremap(screen_info_table, sizeof(*si)); 62 + if (!si) { 63 + pr_err("Could not map screen_info config table\n"); 64 + return; 65 + } 66 + screen_info = *si; 67 + memset(si, 0, sizeof(*si)); 68 + early_memunmap(si, sizeof(*si)); 69 + 70 + if (memblock_is_map_memory(screen_info.lfb_base)) 71 + memblock_mark_nomap(screen_info.lfb_base, 72 + screen_info.lfb_size); 73 + } 63 74 } 64 75 65 76 static int __init uefi_init(u64 efi_system_table)
+5
drivers/firmware/efi/efi.c
··· 58 58 static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; 59 59 static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; 60 60 61 + extern unsigned long screen_info_table; 62 + 61 63 struct mm_struct efi_mm = { 62 64 .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock), 63 65 .mm_users = ATOMIC_INIT(2), ··· 548 546 #endif 549 547 #ifdef CONFIG_EFI_COCO_SECRET 550 548 {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, 549 + #endif 550 + #ifdef CONFIG_EFI_GENERIC_STUB 551 + {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, 551 552 #endif 552 553 {}, 553 554 };
+2 -1
drivers/firmware/efi/libstub/Makefile
··· 81 81 $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE 82 82 $(call if_changed_rule,cc_o_c) 83 83 84 - lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o 84 + lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ 85 + screen_info.o 85 86 86 87 lib-$(CONFIG_ARM) += arm32-stub.o 87 88 lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o
-37
drivers/firmware/efi/libstub/arm32-stub.c
··· 76 76 &efi_entry_state->sctlr_after_ebs); 77 77 } 78 78 79 - static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID; 80 - 81 - struct screen_info *alloc_screen_info(void) 82 - { 83 - struct screen_info *si; 84 - efi_status_t status; 85 - 86 - /* 87 - * Unlike on arm64, where we can directly fill out the screen_info 88 - * structure from the stub, we need to allocate a buffer to hold 89 - * its contents while we hand over to the kernel proper from the 90 - * decompressor. 91 - */ 92 - status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, 93 - sizeof(*si), (void **)&si); 94 - 95 - if (status != EFI_SUCCESS) 96 - return NULL; 97 - 98 - status = efi_bs_call(install_configuration_table, 99 - &screen_info_guid, si); 100 - if (status == EFI_SUCCESS) 101 - return si; 102 - 103 - efi_bs_call(free_pool, si); 104 - return NULL; 105 - } 106 - 107 - void free_screen_info(struct screen_info *si) 108 - { 109 - if (!si) 110 - return; 111 - 112 - efi_bs_call(install_configuration_table, &screen_info_guid, NULL); 113 - efi_bs_call(free_pool, si); 114 - } 115 - 116 79 efi_status_t handle_kernel_image(unsigned long *image_addr, 117 80 unsigned long *image_size, 118 81 unsigned long *reserve_addr,
+9
drivers/firmware/efi/libstub/efi-stub.c
··· 47 47 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; 48 48 static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0); 49 49 50 + struct screen_info * __weak alloc_screen_info(void) 51 + { 52 + return &screen_info; 53 + } 54 + 55 + void __weak free_screen_info(struct screen_info *si) 56 + { 57 + } 58 + 50 59 static struct screen_info *setup_graphics(void) 51 60 { 52 61 efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+3
drivers/firmware/efi/libstub/efistub.h
··· 975 975 976 976 void efi_retrieve_tpm2_eventlog(void); 977 977 978 + struct screen_info *alloc_screen_info(void); 979 + void free_screen_info(struct screen_info *si); 980 + 978 981 #endif
+56
drivers/firmware/efi/libstub/screen_info.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/efi.h> 4 + #include <asm/efi.h> 5 + 6 + #include "efistub.h" 7 + 8 + /* 9 + * There are two ways of populating the core kernel's struct screen_info via the stub: 10 + * - using a configuration table, like below, which relies on the EFI init code 11 + * to locate the table and copy the contents; 12 + * - by linking directly to the core kernel's copy of the global symbol. 13 + * 14 + * The latter is preferred because it makes the EFIFB earlycon available very 15 + * early, but it only works if the EFI stub is part of the core kernel image 16 + * itself. The zboot decompressor can only use the configuration table 17 + * approach. 18 + * 19 + * In order to support both methods from the same build of the EFI stub 20 + * library, provide this dummy global definition of struct screen_info. If it 21 + * is required to satisfy a link dependency, it means we need to override the 22 + * __weak alloc and free methods with the ones below, and those will be pulled 23 + * in as well. 24 + */ 25 + struct screen_info screen_info; 26 + 27 + static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID; 28 + 29 + struct screen_info *alloc_screen_info(void) 30 + { 31 + struct screen_info *si; 32 + efi_status_t status; 33 + 34 + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, 35 + sizeof(*si), (void **)&si); 36 + 37 + if (status != EFI_SUCCESS) 38 + return NULL; 39 + 40 + status = efi_bs_call(install_configuration_table, 41 + &screen_info_guid, si); 42 + if (status == EFI_SUCCESS) 43 + return si; 44 + 45 + efi_bs_call(free_pool, si); 46 + return NULL; 47 + } 48 + 49 + void free_screen_info(struct screen_info *si) 50 + { 51 + if (!si) 52 + return; 53 + 54 + efi_bs_call(install_configuration_table, &screen_info_guid, NULL); 55 + efi_bs_call(free_pool, si); 56 + }
+1 -1
include/linux/efi.h
··· 403 403 * structure that was populated by the stub based on the GOP protocol instance 404 404 * associated with ConOut 405 405 */ 406 - #define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) 406 + #define LINUX_EFI_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) 407 407 #define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989, 0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2) 408 408 #define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f) 409 409 #define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)