Commit 69c1f396 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Ingo Molnar

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: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: default avatarAlexander 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.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent ce9084ba
...@@ -1073,9 +1073,15 @@ ...@@ -1073,9 +1073,15 @@
specified address. The serial port must already be specified address. The serial port must already be
setup and configured. Options are not yet supported. setup and configured. Options are not yet supported.
efifb,[options]
Start an early, unaccelerated console on the EFI
memory mapped framebuffer (if available). On cache
coherent non-x86 systems that use system memory for
the framebuffer, pass the 'ram' option so that it is
mapped with the correct attributes.
earlyprintk= [X86,SH,ARM,M68k,S390] earlyprintk= [X86,SH,ARM,M68k,S390]
earlyprintk=vga earlyprintk=vga
earlyprintk=efi
earlyprintk=sclp earlyprintk=sclp
earlyprintk=xen earlyprintk=xen
earlyprintk=serial[,ttySn[,baudrate]] earlyprintk=serial[,ttySn[,baudrate]]
......
...@@ -40,16 +40,6 @@ config EARLY_PRINTK_DBGP ...@@ -40,16 +40,6 @@ config EARLY_PRINTK_DBGP
with klogd/syslogd or the X server. You should normally say N here, with klogd/syslogd or the X server. You should normally say N here,
unless you want to debug such a crash. You need usb debug device. unless you want to debug such a crash. You need usb debug device.
config EARLY_PRINTK_EFI
bool "Early printk via the EFI framebuffer"
depends on EFI && EARLY_PRINTK
select FONT_SUPPORT
---help---
Write kernel log output directly into the EFI framebuffer.
This is useful for kernel debugging when your machine crashes very
early before the console code is initialized.
config EARLY_PRINTK_USB_XDBC config EARLY_PRINTK_USB_XDBC
bool "Early printk via the xHCI debug port" bool "Early printk via the xHCI debug port"
depends on EARLY_PRINTK && PCI depends on EARLY_PRINTK && PCI
......
...@@ -170,7 +170,6 @@ static inline bool efi_runtime_supported(void) ...@@ -170,7 +170,6 @@ static inline bool efi_runtime_supported(void)
return false; return false;
} }
extern struct console early_efi_console;
extern void parse_efi_setup(u64 phys_addr, u32 data_len); extern void parse_efi_setup(u64 phys_addr, u32 data_len);
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
......
...@@ -388,10 +388,6 @@ static int __init setup_early_printk(char *buf) ...@@ -388,10 +388,6 @@ static int __init setup_early_printk(char *buf)
if (!strncmp(buf, "xen", 3)) if (!strncmp(buf, "xen", 3))
early_console_register(&xenboot_console, keep); early_console_register(&xenboot_console, keep);
#endif #endif
#ifdef CONFIG_EARLY_PRINTK_EFI
if (!strncmp(buf, "efi", 3))
early_console_register(&early_efi_console, keep);
#endif
#ifdef CONFIG_EARLY_PRINTK_USB_XDBC #ifdef CONFIG_EARLY_PRINTK_USB_XDBC
if (!strncmp(buf, "xdbc", 4)) if (!strncmp(buf, "xdbc", 4))
early_xdbc_parse_parameter(buf + 4); early_xdbc_parse_parameter(buf + 4);
......
...@@ -3,5 +3,4 @@ OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y ...@@ -3,5 +3,4 @@ OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y
obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o
...@@ -198,3 +198,9 @@ config EFI_DEV_PATH_PARSER ...@@ -198,3 +198,9 @@ config EFI_DEV_PATH_PARSER
bool bool
depends on ACPI depends on ACPI
default n default n
config EFI_EARLYCON
def_bool y
depends on SERIAL_EARLYCON && !ARM && !IA64
select FONT_SUPPORT
select ARCH_USE_MEMREMAP_PROT
...@@ -30,5 +30,6 @@ arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o ...@@ -30,5 +30,6 @@ arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM) += $(arm-obj-y)
obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y)
obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
// SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2013 Intel Corporation; author Matt Fleming * Copyright (C) 2013 Intel Corporation; author Matt Fleming
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*/ */
#include <linux/console.h> #include <linux/console.h>
...@@ -10,104 +8,68 @@ ...@@ -10,104 +8,68 @@
#include <linux/font.h> #include <linux/font.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/setup.h> #include <linux/serial_core.h>
#include <linux/screen_info.h>
#include <asm/early_ioremap.h>
static const struct font_desc *font; static const struct font_desc *font;
static u32 efi_x, efi_y; static u32 efi_x, efi_y;
static void *efi_fb; static u64 fb_base;
static bool early_efi_keep; static pgprot_t fb_prot;
/*
* efi earlyprintk need use early_ioremap to map the framebuffer.
* But early_ioremap is not usable for earlyprintk=efi,keep, ioremap should
* be used instead. ioremap will be available after paging_init() which is
* earlier than initcall callbacks. Thus adding this early initcall function
* early_efi_map_fb to map the whole efi framebuffer.
*/
static __init int early_efi_map_fb(void)
{
u64 base, size;
if (!early_efi_keep) static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
return 0;
base = boot_params.screen_info.lfb_base;
if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
base |= (u64)boot_params.screen_info.ext_lfb_base << 32;
size = boot_params.screen_info.lfb_size;
efi_fb = ioremap(base, size);
return efi_fb ? 0 : -ENOMEM;
}
early_initcall(early_efi_map_fb);
/*
* early_efi_map maps efi framebuffer region [start, start + len -1]
* In case earlyprintk=efi,keep we have the whole framebuffer mapped already
* so just return the offset efi_fb + start.
*/
static __ref void *early_efi_map(unsigned long start, unsigned long len)
{ {
u64 base; return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
base = boot_params.screen_info.lfb_base;
if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
base |= (u64)boot_params.screen_info.ext_lfb_base << 32;
if (efi_fb)
return (efi_fb + start);
else
return early_ioremap(base + start, len);
} }
static __ref void early_efi_unmap(void *addr, unsigned long len) static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
{ {
if (!efi_fb) early_memunmap(addr, len);
early_iounmap(addr, len);
} }
static void early_efi_clear_scanline(unsigned int y) static void efi_earlycon_clear_scanline(unsigned int y)
{ {
unsigned long *dst; unsigned long *dst;
u16 len; u16 len;
len = boot_params.screen_info.lfb_linelength; len = screen_info.lfb_linelength;
dst = early_efi_map(y*len, len); dst = efi_earlycon_map(y*len, len);
if (!dst) if (!dst)
return; return;
memset(dst, 0, len); memset(dst, 0, len);
early_efi_unmap(dst, len); efi_earlycon_unmap(dst, len);
} }
static void early_efi_scroll_up(void) static void efi_earlycon_scroll_up(void)
{ {
unsigned long *dst, *src; unsigned long *dst, *src;
u16 len; u16 len;
u32 i, height; u32 i, height;
len = boot_params.screen_info.lfb_linelength; len = screen_info.lfb_linelength;
height = boot_params.screen_info.lfb_height; height = screen_info.lfb_height;
for (i = 0; i < height - font->height; i++) { for (i = 0; i < height - font->height; i++) {
dst = early_efi_map(i*len, len); dst = efi_earlycon_map(i*len, len);
if (!dst) if (!dst)
return; return;
src = early_efi_map((i + font->height) * len, len); src = efi_earlycon_map((i + font->height) * len, len);
if (!src) { if (!src) {
early_efi_unmap(dst, len); efi_earlycon_unmap(dst, len);
return; return;
} }
memmove(dst, src, len); memmove(dst, src, len);
early_efi_unmap(src, len); efi_earlycon_unmap(src, len);
early_efi_unmap(dst, len); efi_earlycon_unmap(dst, len);
} }
} }
static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
{ {
const u32 color_black = 0x00000000; const u32 color_black = 0x00000000;
const u32 color_white = 0x00ffffff; const u32 color_white = 0x00ffffff;
...@@ -128,14 +90,14 @@ static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) ...@@ -128,14 +90,14 @@ static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h)
} }
static void static void
early_efi_write(struct console *con, const char *str, unsigned int num) efi_earlycon_write(struct console *con, const char *str, unsigned int num)
{ {
struct screen_info *si; struct screen_info *si;
unsigned int len; unsigned int len;
const char *s; const char *s;
void *dst; void *dst;
si = &boot_params.screen_info; si = &screen_info;
len = si->lfb_linelength; len = si->lfb_linelength;
while (num) { while (num) {
...@@ -155,7 +117,7 @@ early_efi_write(struct console *con, const char *str, unsigned int num) ...@@ -155,7 +117,7 @@ early_efi_write(struct console *con, const char *str, unsigned int num)
for (h = 0; h < font->height; h++) { for (h = 0; h < font->height; h++) {
unsigned int n, x; unsigned int n, x;
dst = early_efi_map((efi_y + h) * len, len); dst = efi_earlycon_map((efi_y + h) * len, len);
if (!dst) if (!dst)
return; return;
...@@ -164,12 +126,12 @@ early_efi_write(struct console *con, const char *str, unsigned int num) ...@@ -164,12 +126,12 @@ early_efi_write(struct console *con, const char *str, unsigned int num)
x = efi_x; x = efi_x;
while (n-- > 0) { while (n-- > 0) {
early_efi_write_char(dst + x*4, *s, h); efi_earlycon_write_char(dst + x*4, *s, h);
x += font->width; x += font->width;
s++; s++;
} }
early_efi_unmap(dst, len); efi_earlycon_unmap(dst, len);
} }
num -= count; num -= count;
...@@ -192,27 +154,40 @@ early_efi_write(struct console *con, const char *str, unsigned int num) ...@@ -192,27 +154,40 @@ early_efi_write(struct console *con, const char *str, unsigned int num)
u32 i; u32 i;
efi_y -= font->height; efi_y -= font->height;
early_efi_scroll_up(); efi_earlycon_scroll_up();
for (i = 0; i < font->height; i++) for (i = 0; i < font->height; i++)
early_efi_clear_scanline(efi_y + i); efi_earlycon_clear_scanline(efi_y + i);
} }
} }
} }
static __init int early_efi_setup(struct console *con, char *options) static int __init efi_earlycon_setup(struct earlycon_device *device,
const char *opt)
{ {
struct screen_info *si; struct screen_info *si;
u16 xres, yres; u16 xres, yres;
u32 i; u32 i;
si = &boot_params.screen_info; if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return -ENODEV;
fb_base = screen_info.lfb_base;
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
fb_base |= (u64)screen_info.ext_lfb_base << 32;
if (opt && !strcmp(opt, "ram"))
fb_prot = PAGE_KERNEL;
else
fb_prot = pgprot_writecombine(PAGE_KERNEL);
si = &screen_info;
xres = si->lfb_width; xres = si->lfb_width;
yres = si->lfb_height; yres = si->lfb_height;
/* /*
* early_efi_write_char() implicitly assumes a framebuffer with * efi_earlycon_write_char() implicitly assumes a framebuffer with
* 32-bits per pixel. * 32 bits per pixel.
*/ */
if (si->lfb_depth != 32) if (si->lfb_depth != 32)
return -ENODEV; return -ENODEV;
...@@ -223,18 +198,9 @@ static __init int early_efi_setup(struct console *con, char *options) ...@@ -223,18 +198,9 @@ static __init int early_efi_setup(struct console *con, char *options)
efi_y = rounddown(yres, font->height) - font->height; efi_y = rounddown(yres, font->height) - font->height;
for (i = 0; i < (yres - efi_y) / font->height; i++) for (i = 0; i < (yres - efi_y) / font->height; i++)
early_efi_scroll_up(); efi_earlycon_scroll_up();
/* early_console_register will unset CON_BOOT in case ,keep */ device->con->write = efi_earlycon_write;
if (!(con->flags & CON_BOOT))
early_efi_keep = true;
return 0; return 0;
} }
EARLYCON_DECLARE(efifb, efi_earlycon_setup);
struct console early_efi_console = {
.name = "earlyefi",
.write = early_efi_write,
.setup = early_efi_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment