diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-10-29 04:21:37 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-10-29 04:21:37 -0400 |
commit | 88392e9dd5a524b8194f1045369c7d0eb16d9f32 (patch) | |
tree | 2eead15738f173ed08ecbedc88adbb0bf2937022 | |
parent | 564c39701c8c52f2310a425223a04d175ed287c4 (diff) | |
parent | 72548e836b0c4abbb652e791dee9c91203a9a4c6 (diff) |
Merge tag 'efi-next' of git://git.kernel.org/pub/scm/linux/kernel/git/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>
-rw-r--r-- | Documentation/kernel-parameters.txt | 8 | ||||
-rw-r--r-- | arch/x86/Kconfig.debug | 10 | ||||
-rw-r--r-- | arch/x86/include/asm/efi.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/early_printk.c | 7 | ||||
-rw-r--r-- | arch/x86/platform/efi/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/efi/early_printk.c | 191 |
6 files changed, 216 insertions, 3 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7f9d4f53882c..7a0f202d482e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -792,6 +792,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
792 | 792 | ||
793 | earlyprintk= [X86,SH,BLACKFIN,ARM] | 793 | earlyprintk= [X86,SH,BLACKFIN,ARM] |
794 | earlyprintk=vga | 794 | earlyprintk=vga |
795 | earlyprintk=efi | ||
795 | earlyprintk=xen | 796 | earlyprintk=xen |
796 | earlyprintk=serial[,ttySn[,baudrate]] | 797 | earlyprintk=serial[,ttySn[,baudrate]] |
797 | earlyprintk=serial[,0x...[,baudrate]] | 798 | earlyprintk=serial[,0x...[,baudrate]] |
@@ -805,7 +806,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
805 | Append ",keep" to not disable it when the real console | 806 | Append ",keep" to not disable it when the real console |
806 | takes over. | 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 | Currently only ttyS0 and ttyS1 may be specified by | 812 | Currently only ttyS0 and ttyS1 may be specified by |
811 | name. Other I/O ports may be explicitly specified | 813 | name. Other I/O ports may be explicitly specified |
@@ -819,8 +821,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
819 | Interaction with the standard serial driver is not | 821 | Interaction with the standard serial driver is not |
820 | very good. | 822 | very good. |
821 | 823 | ||
822 | The VGA output is eventually overwritten by the real | 824 | The VGA and EFI output is eventually overwritten by |
823 | console. | 825 | the real console. |
824 | 826 | ||
825 | The xen output can only be used by Xen PV guests. | 827 | The xen output can only be used by Xen PV guests. |
826 | 828 | ||
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 78d91afb8e50..0f3621ed1db6 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
@@ -59,6 +59,16 @@ config EARLY_PRINTK_DBGP | |||
59 | with klogd/syslogd or the X server. You should normally N here, | 59 | with klogd/syslogd or the X server. You should normally N here, |
60 | unless you want to debug such a crash. You need usb debug device. | 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 | config X86_PTDUMP | 72 | config X86_PTDUMP |
63 | bool "Export kernel pagetable layout to userspace via debugfs" | 73 | bool "Export kernel pagetable layout to userspace via debugfs" |
64 | depends on DEBUG_KERNEL | 74 | depends on DEBUG_KERNEL |
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 0062a0125041..65c6e6e3a552 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h | |||
@@ -109,6 +109,8 @@ static inline bool efi_is_native(void) | |||
109 | return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); | 109 | return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); |
110 | } | 110 | } |
111 | 111 | ||
112 | extern struct console early_efi_console; | ||
113 | |||
112 | #else | 114 | #else |
113 | /* | 115 | /* |
114 | * IF EFI is not configured, have the EFI calls return -ENOSYS. | 116 | * IF EFI is not configured, have the EFI calls return -ENOSYS. |
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index d15f575a861b..6d3d20073615 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <asm/mrst.h> | 17 | #include <asm/mrst.h> |
18 | #include <asm/pgtable.h> | 18 | #include <asm/pgtable.h> |
19 | #include <linux/usb/ehci_def.h> | 19 | #include <linux/usb/ehci_def.h> |
20 | #include <linux/efi.h> | ||
21 | #include <asm/efi.h> | ||
20 | 22 | ||
21 | /* Simple VGA output */ | 23 | /* Simple VGA output */ |
22 | #define VGABASE (__ISA_IO_base + 0xb8000) | 24 | #define VGABASE (__ISA_IO_base + 0xb8000) |
@@ -234,6 +236,11 @@ static int __init setup_early_printk(char *buf) | |||
234 | early_console_register(&early_hsu_console, keep); | 236 | early_console_register(&early_hsu_console, keep); |
235 | } | 237 | } |
236 | #endif | 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 | buf++; | 244 | buf++; |
238 | } | 245 | } |
239 | return 0; | 246 | return 0; |
diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 6db1cc4c7534..b7b0b35c1981 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o | 1 | obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o |
2 | obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o | 2 | obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o |
3 | obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o | ||
diff --git a/arch/x86/platform/efi/early_printk.c b/arch/x86/platform/efi/early_printk.c new file mode 100644 index 000000000000..6599a0027b76 --- /dev/null +++ b/arch/x86/platform/efi/early_printk.c | |||
@@ -0,0 +1,191 @@ | |||
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 | }; | ||