Commit 72548e836b0c4abbb652e791dee9c91203a9a4c6

Authored by Matt Fleming
1 parent c158c3bf59

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>

Showing 6 changed files with 216 additions and 3 deletions Side-by-side Diff

Documentation/kernel-parameters.txt
... ... @@ -792,6 +792,7 @@
792 792  
793 793 earlyprintk= [X86,SH,BLACKFIN,ARM]
794 794 earlyprintk=vga
  795 + earlyprintk=efi
795 796 earlyprintk=xen
796 797 earlyprintk=serial[,ttySn[,baudrate]]
797 798 earlyprintk=serial[,0x...[,baudrate]]
... ... @@ -805,7 +806,8 @@
805 806 Append ",keep" to not disable it when the real console
806 807 takes over.
807 808  
808   - Only vga or serial or usb debug port at a time.
  809 + Only one of vga, efi, serial, or usb debug port can
  810 + be used at a time.
809 811  
810 812 Currently only ttyS0 and ttyS1 may be specified by
811 813 name. Other I/O ports may be explicitly specified
... ... @@ -819,8 +821,8 @@
819 821 Interaction with the standard serial driver is not
820 822 very good.
821 823  
822   - The VGA output is eventually overwritten by the real
823   - console.
  824 + The VGA and EFI output is eventually overwritten by
  825 + the real console.
824 826  
825 827 The xen output can only be used by Xen PV guests.
826 828  
arch/x86/Kconfig.debug
... ... @@ -59,6 +59,16 @@
59 59 with klogd/syslogd or the X server. You should normally N here,
60 60 unless you want to debug such a crash. You need usb debug device.
61 61  
  62 +config EARLY_PRINTK_EFI
  63 + bool "Early printk via the EFI framebuffer"
  64 + depends on EFI && EARLY_PRINTK
  65 + select FONT_SUPPORT
  66 + ---help---
  67 + Write kernel log output directly into the EFI framebuffer.
  68 +
  69 + This is useful for kernel debugging when your machine crashes very
  70 + early before the console code is initialized.
  71 +
62 72 config X86_PTDUMP
63 73 bool "Export kernel pagetable layout to userspace via debugfs"
64 74 depends on DEBUG_KERNEL
arch/x86/include/asm/efi.h
... ... @@ -109,6 +109,8 @@
109 109 return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
110 110 }
111 111  
  112 +extern struct console early_efi_console;
  113 +
112 114 #else
113 115 /*
114 116 * IF EFI is not configured, have the EFI calls return -ENOSYS.
arch/x86/kernel/early_printk.c
... ... @@ -17,6 +17,8 @@
17 17 #include <asm/mrst.h>
18 18 #include <asm/pgtable.h>
19 19 #include <linux/usb/ehci_def.h>
  20 +#include <linux/efi.h>
  21 +#include <asm/efi.h>
20 22  
21 23 /* Simple VGA output */
22 24 #define VGABASE (__ISA_IO_base + 0xb8000)
... ... @@ -234,6 +236,11 @@
234 236 early_console_register(&early_hsu_console, keep);
235 237 }
236 238 #endif
  239 +#ifdef CONFIG_EARLY_PRINTK_EFI
  240 + if (!strncmp(buf, "efi", 3))
  241 + early_console_register(&early_efi_console, keep);
  242 +#endif
  243 +
237 244 buf++;
238 245 }
239 246 return 0;
arch/x86/platform/efi/Makefile
1 1 obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o
2 2 obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
  3 +obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
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 +
  18 +static __init void early_efi_clear_scanline(unsigned int y)
  19 +{
  20 + unsigned long base, *dst;
  21 + u16 len;
  22 +
  23 + base = boot_params.screen_info.lfb_base;
  24 + len = boot_params.screen_info.lfb_linelength;
  25 +
  26 + dst = early_ioremap(base + y*len, len);
  27 + if (!dst)
  28 + return;
  29 +
  30 + memset(dst, 0, len);
  31 + early_iounmap(dst, len);
  32 +}
  33 +
  34 +static __init void early_efi_scroll_up(void)
  35 +{
  36 + unsigned long base, *dst, *src;
  37 + u16 len;
  38 + u32 i, height;
  39 +
  40 + base = boot_params.screen_info.lfb_base;
  41 + len = boot_params.screen_info.lfb_linelength;
  42 + height = boot_params.screen_info.lfb_height;
  43 +
  44 + for (i = 0; i < height - font->height; i++) {
  45 + dst = early_ioremap(base + i*len, len);
  46 + if (!dst)
  47 + return;
  48 +
  49 + src = early_ioremap(base + (i + font->height) * len, len);
  50 + if (!src) {
  51 + early_iounmap(dst, len);
  52 + return;
  53 + }
  54 +
  55 + memmove(dst, src, len);
  56 +
  57 + early_iounmap(src, len);
  58 + early_iounmap(dst, len);
  59 + }
  60 +}
  61 +
  62 +static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h)
  63 +{
  64 + const u32 color_black = 0x00000000;
  65 + const u32 color_white = 0x00ffffff;
  66 + const u8 *src;
  67 + u8 s8;
  68 + int m;
  69 +
  70 + src = font->data + c * font->height;
  71 + s8 = *(src + h);
  72 +
  73 + for (m = 0; m < 8; m++) {
  74 + if ((s8 >> (7 - m)) & 1)
  75 + *dst = color_white;
  76 + else
  77 + *dst = color_black;
  78 + dst++;
  79 + }
  80 +}
  81 +
  82 +static __init void
  83 +early_efi_write(struct console *con, const char *str, unsigned int num)
  84 +{
  85 + struct screen_info *si;
  86 + unsigned long base;
  87 + unsigned int len;
  88 + const char *s;
  89 + void *dst;
  90 +
  91 + base = boot_params.screen_info.lfb_base;
  92 + si = &boot_params.screen_info;
  93 + len = si->lfb_linelength;
  94 +
  95 + while (num) {
  96 + unsigned int linemax;
  97 + unsigned int h, count = 0;
  98 +
  99 + for (s = str; *s && *s != '\n'; s++) {
  100 + if (count == num)
  101 + break;
  102 + count++;
  103 + }
  104 +
  105 + linemax = (si->lfb_width - efi_x) / font->width;
  106 + if (count > linemax)
  107 + count = linemax;
  108 +
  109 + for (h = 0; h < font->height; h++) {
  110 + unsigned int n, x;
  111 +
  112 + dst = early_ioremap(base + (efi_y + h) * len, len);
  113 + if (!dst)
  114 + return;
  115 +
  116 + s = str;
  117 + n = count;
  118 + x = efi_x;
  119 +
  120 + while (n-- > 0) {
  121 + early_efi_write_char(dst + x*4, *s, h);
  122 + x += font->width;
  123 + s++;
  124 + }
  125 +
  126 + early_iounmap(dst, len);
  127 + }
  128 +
  129 + num -= count;
  130 + efi_x += count * font->width;
  131 + str += count;
  132 +
  133 + if (num > 0 && *s == '\n') {
  134 + efi_x = 0;
  135 + efi_y += font->height;
  136 + str++;
  137 + num--;
  138 + }
  139 +
  140 + if (efi_x >= si->lfb_width) {
  141 + efi_x = 0;
  142 + efi_y += font->height;
  143 + }
  144 +
  145 + if (efi_y + font->height >= si->lfb_height) {
  146 + u32 i;
  147 +
  148 + efi_y -= font->height;
  149 + early_efi_scroll_up();
  150 +
  151 + for (i = 0; i < font->height; i++)
  152 + early_efi_clear_scanline(efi_y + i);
  153 + }
  154 + }
  155 +}
  156 +
  157 +static __init int early_efi_setup(struct console *con, char *options)
  158 +{
  159 + struct screen_info *si;
  160 + u16 xres, yres;
  161 + u32 i;
  162 +
  163 + si = &boot_params.screen_info;
  164 + xres = si->lfb_width;
  165 + yres = si->lfb_height;
  166 +
  167 + /*
  168 + * early_efi_write_char() implicitly assumes a framebuffer with
  169 + * 32-bits per pixel.
  170 + */
  171 + if (si->lfb_depth != 32)
  172 + return -ENODEV;
  173 +
  174 + font = get_default_font(xres, yres, -1, -1);
  175 + if (!font)
  176 + return -ENODEV;
  177 +
  178 + efi_y = rounddown(yres, font->height) - font->height;
  179 + for (i = 0; i < (yres - efi_y) / font->height; i++)
  180 + early_efi_scroll_up();
  181 +
  182 + return 0;
  183 +}
  184 +
  185 +struct console early_efi_console = {
  186 + .name = "earlyefi",
  187 + .write = early_efi_write,
  188 + .setup = early_efi_setup,
  189 + .flags = CON_PRINTBUFFER,
  190 + .index = -1,
  191 +};