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

efi/x86: Convert x86 EFI earlyprintk into generic earlycon implementation

Move the x86 EFI earlyprintk implementation to a shared location under
drivers/firmware and tweak it slightly so we can expose it as an earlycon
implementation (which is generic) rather than earlyprintk (which is only
implemented for a few architectures)

This also involves switching to write-combine mappings by default (which
is required on ARM since device mappings lack memory semantics, and so
memcpy/memset may not be used on them), and adding support for shared
memory framebuffers on cache coherent non-x86 systems (which do not
tolerate mismatched attributes).

Note that 32-bit ARM does not populate its struct screen_info early
enough for earlycon=efifb to work, so it is disabled there.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Alexander Graf <agraf@suse.de>
Cc: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: Jeffrey Hugo <jhugo@codeaurora.org>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Cc: Peter Jones <pjones@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-efi@vger.kernel.org
Link: http://lkml.kernel.org/r/20190202094119.13230-10-ard.biesheuvel@linaro.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Ard Biesheuvel and committed by
Ingo Molnar
69c1f396 ce9084ba

+220 -257
+7 -1
Documentation/admin-guide/kernel-parameters.txt
··· 1073 1073 specified address. The serial port must already be 1074 1074 setup and configured. Options are not yet supported. 1075 1075 1076 + efifb,[options] 1077 + Start an early, unaccelerated console on the EFI 1078 + memory mapped framebuffer (if available). On cache 1079 + coherent non-x86 systems that use system memory for 1080 + the framebuffer, pass the 'ram' option so that it is 1081 + mapped with the correct attributes. 1082 + 1076 1083 earlyprintk= [X86,SH,ARM,M68k,S390] 1077 1084 earlyprintk=vga 1078 - earlyprintk=efi 1079 1085 earlyprintk=sclp 1080 1086 earlyprintk=xen 1081 1087 earlyprintk=serial[,ttySn[,baudrate]]
-10
arch/x86/Kconfig.debug
··· 40 40 with klogd/syslogd or the X server. You should normally say N here, 41 41 unless you want to debug such a crash. You need usb debug device. 42 42 43 - config EARLY_PRINTK_EFI 44 - bool "Early printk via the EFI framebuffer" 45 - depends on EFI && EARLY_PRINTK 46 - select FONT_SUPPORT 47 - ---help--- 48 - Write kernel log output directly into the EFI framebuffer. 49 - 50 - This is useful for kernel debugging when your machine crashes very 51 - early before the console code is initialized. 52 - 53 43 config EARLY_PRINTK_USB_XDBC 54 44 bool "Early printk via the xHCI debug port" 55 45 depends on EARLY_PRINTK && PCI
-1
arch/x86/include/asm/efi.h
··· 170 170 return false; 171 171 } 172 172 173 - extern struct console early_efi_console; 174 173 extern void parse_efi_setup(u64 phys_addr, u32 data_len); 175 174 176 175 extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
-4
arch/x86/kernel/early_printk.c
··· 388 388 if (!strncmp(buf, "xen", 3)) 389 389 early_console_register(&xenboot_console, keep); 390 390 #endif 391 - #ifdef CONFIG_EARLY_PRINTK_EFI 392 - if (!strncmp(buf, "efi", 3)) 393 - early_console_register(&early_efi_console, keep); 394 - #endif 395 391 #ifdef CONFIG_EARLY_PRINTK_USB_XDBC 396 392 if (!strncmp(buf, "xdbc", 4)) 397 393 early_xdbc_parse_parameter(buf + 4);
-1
arch/x86/platform/efi/Makefile
··· 3 3 OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y 4 4 5 5 obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o 6 - obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o 7 6 obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o
-240
arch/x86/platform/efi/early_printk.c
··· 1 - /* 2 - * Copyright (C) 2013 Intel Corporation; author Matt Fleming 3 - * 4 - * This file is part of the Linux kernel, and is made available under 5 - * the terms of the GNU General Public License version 2. 6 - */ 7 - 8 - #include <linux/console.h> 9 - #include <linux/efi.h> 10 - #include <linux/font.h> 11 - #include <linux/io.h> 12 - #include <linux/kernel.h> 13 - #include <asm/setup.h> 14 - 15 - static const struct font_desc *font; 16 - static u32 efi_x, efi_y; 17 - static void *efi_fb; 18 - static bool early_efi_keep; 19 - 20 - /* 21 - * efi earlyprintk need use early_ioremap to map the framebuffer. 22 - * But early_ioremap is not usable for earlyprintk=efi,keep, ioremap should 23 - * be used instead. ioremap will be available after paging_init() which is 24 - * earlier than initcall callbacks. Thus adding this early initcall function 25 - * early_efi_map_fb to map the whole efi framebuffer. 26 - */ 27 - static __init int early_efi_map_fb(void) 28 - { 29 - u64 base, size; 30 - 31 - if (!early_efi_keep) 32 - return 0; 33 - 34 - base = boot_params.screen_info.lfb_base; 35 - if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) 36 - base |= (u64)boot_params.screen_info.ext_lfb_base << 32; 37 - size = boot_params.screen_info.lfb_size; 38 - efi_fb = ioremap(base, size); 39 - 40 - return efi_fb ? 0 : -ENOMEM; 41 - } 42 - early_initcall(early_efi_map_fb); 43 - 44 - /* 45 - * early_efi_map maps efi framebuffer region [start, start + len -1] 46 - * In case earlyprintk=efi,keep we have the whole framebuffer mapped already 47 - * so just return the offset efi_fb + start. 48 - */ 49 - static __ref void *early_efi_map(unsigned long start, unsigned long len) 50 - { 51 - u64 base; 52 - 53 - base = boot_params.screen_info.lfb_base; 54 - if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) 55 - base |= (u64)boot_params.screen_info.ext_lfb_base << 32; 56 - 57 - if (efi_fb) 58 - return (efi_fb + start); 59 - else 60 - return early_ioremap(base + start, len); 61 - } 62 - 63 - static __ref void early_efi_unmap(void *addr, unsigned long len) 64 - { 65 - if (!efi_fb) 66 - early_iounmap(addr, len); 67 - } 68 - 69 - static void early_efi_clear_scanline(unsigned int y) 70 - { 71 - unsigned long *dst; 72 - u16 len; 73 - 74 - len = boot_params.screen_info.lfb_linelength; 75 - dst = early_efi_map(y*len, len); 76 - if (!dst) 77 - return; 78 - 79 - memset(dst, 0, len); 80 - early_efi_unmap(dst, len); 81 - } 82 - 83 - static void early_efi_scroll_up(void) 84 - { 85 - unsigned long *dst, *src; 86 - u16 len; 87 - u32 i, height; 88 - 89 - len = boot_params.screen_info.lfb_linelength; 90 - height = boot_params.screen_info.lfb_height; 91 - 92 - for (i = 0; i < height - font->height; i++) { 93 - dst = early_efi_map(i*len, len); 94 - if (!dst) 95 - return; 96 - 97 - src = early_efi_map((i + font->height) * len, len); 98 - if (!src) { 99 - early_efi_unmap(dst, len); 100 - return; 101 - } 102 - 103 - memmove(dst, src, len); 104 - 105 - early_efi_unmap(src, len); 106 - early_efi_unmap(dst, len); 107 - } 108 - } 109 - 110 - static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) 111 - { 112 - const u32 color_black = 0x00000000; 113 - const u32 color_white = 0x00ffffff; 114 - const u8 *src; 115 - u8 s8; 116 - int m; 117 - 118 - src = font->data + c * font->height; 119 - s8 = *(src + h); 120 - 121 - for (m = 0; m < 8; m++) { 122 - if ((s8 >> (7 - m)) & 1) 123 - *dst = color_white; 124 - else 125 - *dst = color_black; 126 - dst++; 127 - } 128 - } 129 - 130 - static void 131 - early_efi_write(struct console *con, const char *str, unsigned int num) 132 - { 133 - struct screen_info *si; 134 - unsigned int len; 135 - const char *s; 136 - void *dst; 137 - 138 - si = &boot_params.screen_info; 139 - len = si->lfb_linelength; 140 - 141 - while (num) { 142 - unsigned int linemax; 143 - unsigned int h, count = 0; 144 - 145 - for (s = str; *s && *s != '\n'; s++) { 146 - if (count == num) 147 - break; 148 - count++; 149 - } 150 - 151 - linemax = (si->lfb_width - efi_x) / font->width; 152 - if (count > linemax) 153 - count = linemax; 154 - 155 - for (h = 0; h < font->height; h++) { 156 - unsigned int n, x; 157 - 158 - dst = early_efi_map((efi_y + h) * len, len); 159 - if (!dst) 160 - return; 161 - 162 - s = str; 163 - n = count; 164 - x = efi_x; 165 - 166 - while (n-- > 0) { 167 - early_efi_write_char(dst + x*4, *s, h); 168 - x += font->width; 169 - s++; 170 - } 171 - 172 - early_efi_unmap(dst, len); 173 - } 174 - 175 - num -= count; 176 - efi_x += count * font->width; 177 - str += count; 178 - 179 - if (num > 0 && *s == '\n') { 180 - efi_x = 0; 181 - efi_y += font->height; 182 - str++; 183 - num--; 184 - } 185 - 186 - if (efi_x + font->width > si->lfb_width) { 187 - efi_x = 0; 188 - efi_y += font->height; 189 - } 190 - 191 - if (efi_y + font->height > si->lfb_height) { 192 - u32 i; 193 - 194 - efi_y -= font->height; 195 - early_efi_scroll_up(); 196 - 197 - for (i = 0; i < font->height; i++) 198 - early_efi_clear_scanline(efi_y + i); 199 - } 200 - } 201 - } 202 - 203 - static __init int early_efi_setup(struct console *con, char *options) 204 - { 205 - struct screen_info *si; 206 - u16 xres, yres; 207 - u32 i; 208 - 209 - si = &boot_params.screen_info; 210 - xres = si->lfb_width; 211 - yres = si->lfb_height; 212 - 213 - /* 214 - * early_efi_write_char() implicitly assumes a framebuffer with 215 - * 32-bits per pixel. 216 - */ 217 - if (si->lfb_depth != 32) 218 - return -ENODEV; 219 - 220 - font = get_default_font(xres, yres, -1, -1); 221 - if (!font) 222 - return -ENODEV; 223 - 224 - efi_y = rounddown(yres, font->height) - font->height; 225 - for (i = 0; i < (yres - efi_y) / font->height; i++) 226 - early_efi_scroll_up(); 227 - 228 - /* early_console_register will unset CON_BOOT in case ,keep */ 229 - if (!(con->flags & CON_BOOT)) 230 - early_efi_keep = true; 231 - return 0; 232 - } 233 - 234 - struct console early_efi_console = { 235 - .name = "earlyefi", 236 - .write = early_efi_write, 237 - .setup = early_efi_setup, 238 - .flags = CON_PRINTBUFFER, 239 - .index = -1, 240 - };
+6
drivers/firmware/efi/Kconfig
··· 198 198 bool 199 199 depends on ACPI 200 200 default n 201 + 202 + config EFI_EARLYCON 203 + def_bool y 204 + depends on SERIAL_EARLYCON && !ARM && !IA64 205 + select FONT_SUPPORT 206 + select ARCH_USE_MEMREMAP_PROT
+1
drivers/firmware/efi/Makefile
··· 30 30 obj-$(CONFIG_ARM) += $(arm-obj-y) 31 31 obj-$(CONFIG_ARM64) += $(arm-obj-y) 32 32 obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o 33 + obj-$(CONFIG_EFI_EARLYCON) += earlycon.o 33 34 obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o 34 35 obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
+206
drivers/firmware/efi/earlycon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2013 Intel Corporation; author Matt Fleming 4 + */ 5 + 6 + #include <linux/console.h> 7 + #include <linux/efi.h> 8 + #include <linux/font.h> 9 + #include <linux/io.h> 10 + #include <linux/kernel.h> 11 + #include <linux/serial_core.h> 12 + #include <linux/screen_info.h> 13 + 14 + #include <asm/early_ioremap.h> 15 + 16 + static const struct font_desc *font; 17 + static u32 efi_x, efi_y; 18 + static u64 fb_base; 19 + static pgprot_t fb_prot; 20 + 21 + static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) 22 + { 23 + return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); 24 + } 25 + 26 + static __ref void efi_earlycon_unmap(void *addr, unsigned long len) 27 + { 28 + early_memunmap(addr, len); 29 + } 30 + 31 + static void efi_earlycon_clear_scanline(unsigned int y) 32 + { 33 + unsigned long *dst; 34 + u16 len; 35 + 36 + len = screen_info.lfb_linelength; 37 + dst = efi_earlycon_map(y*len, len); 38 + if (!dst) 39 + return; 40 + 41 + memset(dst, 0, len); 42 + efi_earlycon_unmap(dst, len); 43 + } 44 + 45 + static void efi_earlycon_scroll_up(void) 46 + { 47 + unsigned long *dst, *src; 48 + u16 len; 49 + u32 i, height; 50 + 51 + len = screen_info.lfb_linelength; 52 + height = screen_info.lfb_height; 53 + 54 + for (i = 0; i < height - font->height; i++) { 55 + dst = efi_earlycon_map(i*len, len); 56 + if (!dst) 57 + return; 58 + 59 + src = efi_earlycon_map((i + font->height) * len, len); 60 + if (!src) { 61 + efi_earlycon_unmap(dst, len); 62 + return; 63 + } 64 + 65 + memmove(dst, src, len); 66 + 67 + efi_earlycon_unmap(src, len); 68 + efi_earlycon_unmap(dst, len); 69 + } 70 + } 71 + 72 + static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) 73 + { 74 + const u32 color_black = 0x00000000; 75 + const u32 color_white = 0x00ffffff; 76 + const u8 *src; 77 + u8 s8; 78 + int m; 79 + 80 + src = font->data + c * font->height; 81 + s8 = *(src + h); 82 + 83 + for (m = 0; m < 8; m++) { 84 + if ((s8 >> (7 - m)) & 1) 85 + *dst = color_white; 86 + else 87 + *dst = color_black; 88 + dst++; 89 + } 90 + } 91 + 92 + static void 93 + efi_earlycon_write(struct console *con, const char *str, unsigned int num) 94 + { 95 + struct screen_info *si; 96 + unsigned int len; 97 + const char *s; 98 + void *dst; 99 + 100 + si = &screen_info; 101 + len = si->lfb_linelength; 102 + 103 + while (num) { 104 + unsigned int linemax; 105 + unsigned int h, count = 0; 106 + 107 + for (s = str; *s && *s != '\n'; s++) { 108 + if (count == num) 109 + break; 110 + count++; 111 + } 112 + 113 + linemax = (si->lfb_width - efi_x) / font->width; 114 + if (count > linemax) 115 + count = linemax; 116 + 117 + for (h = 0; h < font->height; h++) { 118 + unsigned int n, x; 119 + 120 + dst = efi_earlycon_map((efi_y + h) * len, len); 121 + if (!dst) 122 + return; 123 + 124 + s = str; 125 + n = count; 126 + x = efi_x; 127 + 128 + while (n-- > 0) { 129 + efi_earlycon_write_char(dst + x*4, *s, h); 130 + x += font->width; 131 + s++; 132 + } 133 + 134 + efi_earlycon_unmap(dst, len); 135 + } 136 + 137 + num -= count; 138 + efi_x += count * font->width; 139 + str += count; 140 + 141 + if (num > 0 && *s == '\n') { 142 + efi_x = 0; 143 + efi_y += font->height; 144 + str++; 145 + num--; 146 + } 147 + 148 + if (efi_x + font->width > si->lfb_width) { 149 + efi_x = 0; 150 + efi_y += font->height; 151 + } 152 + 153 + if (efi_y + font->height > si->lfb_height) { 154 + u32 i; 155 + 156 + efi_y -= font->height; 157 + efi_earlycon_scroll_up(); 158 + 159 + for (i = 0; i < font->height; i++) 160 + efi_earlycon_clear_scanline(efi_y + i); 161 + } 162 + } 163 + } 164 + 165 + static int __init efi_earlycon_setup(struct earlycon_device *device, 166 + const char *opt) 167 + { 168 + struct screen_info *si; 169 + u16 xres, yres; 170 + u32 i; 171 + 172 + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) 173 + return -ENODEV; 174 + 175 + fb_base = screen_info.lfb_base; 176 + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) 177 + fb_base |= (u64)screen_info.ext_lfb_base << 32; 178 + 179 + if (opt && !strcmp(opt, "ram")) 180 + fb_prot = PAGE_KERNEL; 181 + else 182 + fb_prot = pgprot_writecombine(PAGE_KERNEL); 183 + 184 + si = &screen_info; 185 + xres = si->lfb_width; 186 + yres = si->lfb_height; 187 + 188 + /* 189 + * efi_earlycon_write_char() implicitly assumes a framebuffer with 190 + * 32 bits per pixel. 191 + */ 192 + if (si->lfb_depth != 32) 193 + return -ENODEV; 194 + 195 + font = get_default_font(xres, yres, -1, -1); 196 + if (!font) 197 + return -ENODEV; 198 + 199 + efi_y = rounddown(yres, font->height) - font->height; 200 + for (i = 0; i < (yres - efi_y) / font->height; i++) 201 + efi_earlycon_scroll_up(); 202 + 203 + device->con->write = efi_earlycon_write; 204 + return 0; 205 + } 206 + EARLYCON_DECLARE(efifb, efi_earlycon_setup);