-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86/efi: Add EFI framebuffer earlyprintk support
It's incredibly difficult to diagnose early EFI boot issues without special hardware because earlyprintk=vga doesn't work on EFI systems. Add support for writing to the EFI framebuffer, via earlyprintk=efi, which will actually give users a chance of providing debug output. Cc: H. Peter Anvin <hpa@zytor.com> Acked-by: Ingo Molnar <mingo@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Jones <pjones@redhat.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
- Loading branch information
Matt Fleming
committed
Oct 28, 2013
1 parent
c158c3b
commit 72548e8
Showing
6 changed files
with
216 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |