Commit 72548e836b0c4abbb652e791dee9c91203a9a4c6
1 parent
c158c3bf59
Exists in
master
and in
16 other branches
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
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
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 | +}; |