Skip to content

Commit

Permalink
Merge tag 'efi-next' of git://git.kernel.org/pub/scm/linux/kernel/git…
Browse files Browse the repository at this point in the history
…/mfleming/efi into x86/efi

Pull EFI earlyprintk support from Matt Fleming:

 " * Add support for earlyprintk=efi which uses the EFI framebuffer. Very
     useful for debugging boot issues. "

Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Ingo Molnar committed Oct 29, 2013
2 parents 564c397 + 72548e8 commit 88392e9
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 3 deletions.
8 changes: 5 additions & 3 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.

earlyprintk= [X86,SH,BLACKFIN,ARM]
earlyprintk=vga
earlyprintk=efi
earlyprintk=xen
earlyprintk=serial[,ttySn[,baudrate]]
earlyprintk=serial[,0x...[,baudrate]]
Expand All @@ -805,7 +806,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Append ",keep" to not disable it when the real console
takes over.

Only vga or serial or usb debug port at a time.
Only one of vga, efi, serial, or usb debug port can
be used at a time.

Currently only ttyS0 and ttyS1 may be specified by
name. Other I/O ports may be explicitly specified
Expand All @@ -819,8 +821,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Interaction with the standard serial driver is not
very good.

The VGA output is eventually overwritten by the real
console.
The VGA and EFI output is eventually overwritten by
the real console.

The xen output can only be used by Xen PV guests.

Expand Down
10 changes: 10 additions & 0 deletions arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ config EARLY_PRINTK_DBGP
with klogd/syslogd or the X server. You should normally N here,
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 X86_PTDUMP
bool "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ static inline bool efi_is_native(void)
return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
}

extern struct console early_efi_console;

#else
/*
* IF EFI is not configured, have the EFI calls return -ENOSYS.
Expand Down
7 changes: 7 additions & 0 deletions arch/x86/kernel/early_printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <asm/mrst.h>
#include <asm/pgtable.h>
#include <linux/usb/ehci_def.h>
#include <linux/efi.h>
#include <asm/efi.h>

/* Simple VGA output */
#define VGABASE (__ISA_IO_base + 0xb8000)
Expand Down Expand Up @@ -234,6 +236,11 @@ static int __init setup_early_printk(char *buf)
early_console_register(&early_hsu_console, keep);
}
#endif
#ifdef CONFIG_EARLY_PRINTK_EFI
if (!strncmp(buf, "efi", 3))
early_console_register(&early_efi_console, keep);
#endif

buf++;
}
return 0;
Expand Down
1 change: 1 addition & 0 deletions arch/x86/platform/efi/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
191 changes: 191 additions & 0 deletions arch/x86/platform/efi/early_printk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* 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/efi.h>
#include <linux/font.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <asm/setup.h>

static const struct font_desc *font;
static u32 efi_x, efi_y;

static __init void early_efi_clear_scanline(unsigned int y)
{
unsigned long base, *dst;
u16 len;

base = boot_params.screen_info.lfb_base;
len = boot_params.screen_info.lfb_linelength;

dst = early_ioremap(base + y*len, len);
if (!dst)
return;

memset(dst, 0, len);
early_iounmap(dst, len);
}

static __init void early_efi_scroll_up(void)
{
unsigned long base, *dst, *src;
u16 len;
u32 i, height;

base = boot_params.screen_info.lfb_base;
len = boot_params.screen_info.lfb_linelength;
height = boot_params.screen_info.lfb_height;

for (i = 0; i < height - font->height; i++) {
dst = early_ioremap(base + i*len, len);
if (!dst)
return;

src = early_ioremap(base + (i + font->height) * len, len);
if (!src) {
early_iounmap(dst, len);
return;
}

memmove(dst, src, len);

early_iounmap(src, len);
early_iounmap(dst, len);
}
}

static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h)
{
const u32 color_black = 0x00000000;
const u32 color_white = 0x00ffffff;
const u8 *src;
u8 s8;
int m;

src = font->data + c * font->height;
s8 = *(src + h);

for (m = 0; m < 8; m++) {
if ((s8 >> (7 - m)) & 1)
*dst = color_white;
else
*dst = color_black;
dst++;
}
}

static __init void
early_efi_write(struct console *con, const char *str, unsigned int num)
{
struct screen_info *si;
unsigned long base;
unsigned int len;
const char *s;
void *dst;

base = boot_params.screen_info.lfb_base;
si = &boot_params.screen_info;
len = si->lfb_linelength;

while (num) {
unsigned int linemax;
unsigned int h, count = 0;

for (s = str; *s && *s != '\n'; s++) {
if (count == num)
break;
count++;
}

linemax = (si->lfb_width - efi_x) / font->width;
if (count > linemax)
count = linemax;

for (h = 0; h < font->height; h++) {
unsigned int n, x;

dst = early_ioremap(base + (efi_y + h) * len, len);
if (!dst)
return;

s = str;
n = count;
x = efi_x;

while (n-- > 0) {
early_efi_write_char(dst + x*4, *s, h);
x += font->width;
s++;
}

early_iounmap(dst, len);
}

num -= count;
efi_x += count * font->width;
str += count;

if (num > 0 && *s == '\n') {
efi_x = 0;
efi_y += font->height;
str++;
num--;
}

if (efi_x >= si->lfb_width) {
efi_x = 0;
efi_y += font->height;
}

if (efi_y + font->height >= si->lfb_height) {
u32 i;

efi_y -= font->height;
early_efi_scroll_up();

for (i = 0; i < font->height; i++)
early_efi_clear_scanline(efi_y + i);
}
}
}

static __init int early_efi_setup(struct console *con, char *options)
{
struct screen_info *si;
u16 xres, yres;
u32 i;

si = &boot_params.screen_info;
xres = si->lfb_width;
yres = si->lfb_height;

/*
* early_efi_write_char() implicitly assumes a framebuffer with
* 32-bits per pixel.
*/
if (si->lfb_depth != 32)
return -ENODEV;

font = get_default_font(xres, yres, -1, -1);
if (!font)
return -ENODEV;

efi_y = rounddown(yres, font->height) - font->height;
for (i = 0; i < (yres - efi_y) / font->height; i++)
early_efi_scroll_up();

return 0;
}

struct console early_efi_console = {
.name = "earlyefi",
.write = early_efi_write,
.setup = early_efi_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};

0 comments on commit 88392e9

Please sign in to comment.