diff options
Diffstat (limited to 'arch/x86/platform/efi/early_printk.c')
-rw-r--r-- | arch/x86/platform/efi/early_printk.c | 191 |
1 files changed, 191 insertions, 0 deletions
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 | }; | ||