Commit | Line | Data |
---|---|---|
72548e83 MF |
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; | |
5f35eb0e DY |
17 | static void *efi_fb; |
18 | static bool early_efi_keep; | |
72548e83 | 19 | |
5f35eb0e DY |
20 | /* |
21 | * efi earlyprintk need use early_ioremap to map the framebuffer. | |
22 | * But early_ioremap is not usable for earlyprintk=efi,keep, ioremap should | |
23 | * be used instead. ioremap will be available after paging_init() which is | |
24 | * earlier than initcall callbacks. Thus adding this early initcall function | |
25 | * early_efi_map_fb to map the whole efi framebuffer. | |
26 | */ | |
27 | static __init int early_efi_map_fb(void) | |
72548e83 | 28 | { |
5f35eb0e DY |
29 | unsigned long base, size; |
30 | ||
31 | if (!early_efi_keep) | |
32 | return 0; | |
72548e83 MF |
33 | |
34 | base = boot_params.screen_info.lfb_base; | |
5f35eb0e DY |
35 | size = boot_params.screen_info.lfb_size; |
36 | efi_fb = ioremap(base, size); | |
37 | ||
38 | return efi_fb ? 0 : -ENOMEM; | |
39 | } | |
40 | early_initcall(early_efi_map_fb); | |
41 | ||
42 | /* | |
43 | * early_efi_map maps efi framebuffer region [start, start + len -1] | |
44 | * In case earlyprintk=efi,keep we have the whole framebuffer mapped already | |
45 | * so just return the offset efi_fb + start. | |
46 | */ | |
bd721ea7 | 47 | static __ref void *early_efi_map(unsigned long start, unsigned long len) |
5f35eb0e DY |
48 | { |
49 | unsigned long base; | |
50 | ||
51 | base = boot_params.screen_info.lfb_base; | |
52 | ||
53 | if (efi_fb) | |
54 | return (efi_fb + start); | |
55 | else | |
56 | return early_ioremap(base + start, len); | |
57 | } | |
72548e83 | 58 | |
bd721ea7 | 59 | static __ref void early_efi_unmap(void *addr, unsigned long len) |
5f35eb0e DY |
60 | { |
61 | if (!efi_fb) | |
62 | early_iounmap(addr, len); | |
63 | } | |
64 | ||
65 | static void early_efi_clear_scanline(unsigned int y) | |
66 | { | |
67 | unsigned long *dst; | |
68 | u16 len; | |
69 | ||
70 | len = boot_params.screen_info.lfb_linelength; | |
71 | dst = early_efi_map(y*len, len); | |
72548e83 MF |
72 | if (!dst) |
73 | return; | |
74 | ||
75 | memset(dst, 0, len); | |
5f35eb0e | 76 | early_efi_unmap(dst, len); |
72548e83 MF |
77 | } |
78 | ||
5f35eb0e | 79 | static void early_efi_scroll_up(void) |
72548e83 | 80 | { |
5f35eb0e | 81 | unsigned long *dst, *src; |
72548e83 MF |
82 | u16 len; |
83 | u32 i, height; | |
84 | ||
72548e83 MF |
85 | len = boot_params.screen_info.lfb_linelength; |
86 | height = boot_params.screen_info.lfb_height; | |
87 | ||
88 | for (i = 0; i < height - font->height; i++) { | |
5f35eb0e | 89 | dst = early_efi_map(i*len, len); |
72548e83 MF |
90 | if (!dst) |
91 | return; | |
92 | ||
5f35eb0e | 93 | src = early_efi_map((i + font->height) * len, len); |
72548e83 | 94 | if (!src) { |
5f35eb0e | 95 | early_efi_unmap(dst, len); |
72548e83 MF |
96 | return; |
97 | } | |
98 | ||
99 | memmove(dst, src, len); | |
100 | ||
5f35eb0e DY |
101 | early_efi_unmap(src, len); |
102 | early_efi_unmap(dst, len); | |
72548e83 MF |
103 | } |
104 | } | |
105 | ||
106 | static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) | |
107 | { | |
108 | const u32 color_black = 0x00000000; | |
109 | const u32 color_white = 0x00ffffff; | |
110 | const u8 *src; | |
111 | u8 s8; | |
112 | int m; | |
113 | ||
114 | src = font->data + c * font->height; | |
115 | s8 = *(src + h); | |
116 | ||
117 | for (m = 0; m < 8; m++) { | |
118 | if ((s8 >> (7 - m)) & 1) | |
119 | *dst = color_white; | |
120 | else | |
121 | *dst = color_black; | |
122 | dst++; | |
123 | } | |
124 | } | |
125 | ||
5f35eb0e | 126 | static void |
72548e83 MF |
127 | early_efi_write(struct console *con, const char *str, unsigned int num) |
128 | { | |
129 | struct screen_info *si; | |
72548e83 MF |
130 | unsigned int len; |
131 | const char *s; | |
132 | void *dst; | |
133 | ||
72548e83 MF |
134 | si = &boot_params.screen_info; |
135 | len = si->lfb_linelength; | |
136 | ||
137 | while (num) { | |
138 | unsigned int linemax; | |
139 | unsigned int h, count = 0; | |
140 | ||
141 | for (s = str; *s && *s != '\n'; s++) { | |
142 | if (count == num) | |
143 | break; | |
144 | count++; | |
145 | } | |
146 | ||
147 | linemax = (si->lfb_width - efi_x) / font->width; | |
148 | if (count > linemax) | |
149 | count = linemax; | |
150 | ||
151 | for (h = 0; h < font->height; h++) { | |
152 | unsigned int n, x; | |
153 | ||
5f35eb0e | 154 | dst = early_efi_map((efi_y + h) * len, len); |
72548e83 MF |
155 | if (!dst) |
156 | return; | |
157 | ||
158 | s = str; | |
159 | n = count; | |
160 | x = efi_x; | |
161 | ||
162 | while (n-- > 0) { | |
163 | early_efi_write_char(dst + x*4, *s, h); | |
164 | x += font->width; | |
165 | s++; | |
166 | } | |
167 | ||
5f35eb0e | 168 | early_efi_unmap(dst, len); |
72548e83 MF |
169 | } |
170 | ||
171 | num -= count; | |
172 | efi_x += count * font->width; | |
173 | str += count; | |
174 | ||
175 | if (num > 0 && *s == '\n') { | |
176 | efi_x = 0; | |
177 | efi_y += font->height; | |
178 | str++; | |
179 | num--; | |
180 | } | |
181 | ||
182 | if (efi_x >= si->lfb_width) { | |
183 | efi_x = 0; | |
184 | efi_y += font->height; | |
185 | } | |
186 | ||
1f3a8bae | 187 | if (efi_y + font->height > si->lfb_height) { |
72548e83 MF |
188 | u32 i; |
189 | ||
190 | efi_y -= font->height; | |
191 | early_efi_scroll_up(); | |
192 | ||
193 | for (i = 0; i < font->height; i++) | |
194 | early_efi_clear_scanline(efi_y + i); | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | static __init int early_efi_setup(struct console *con, char *options) | |
200 | { | |
201 | struct screen_info *si; | |
202 | u16 xres, yres; | |
203 | u32 i; | |
204 | ||
205 | si = &boot_params.screen_info; | |
206 | xres = si->lfb_width; | |
207 | yres = si->lfb_height; | |
208 | ||
209 | /* | |
210 | * early_efi_write_char() implicitly assumes a framebuffer with | |
211 | * 32-bits per pixel. | |
212 | */ | |
213 | if (si->lfb_depth != 32) | |
214 | return -ENODEV; | |
215 | ||
216 | font = get_default_font(xres, yres, -1, -1); | |
217 | if (!font) | |
218 | return -ENODEV; | |
219 | ||
220 | efi_y = rounddown(yres, font->height) - font->height; | |
221 | for (i = 0; i < (yres - efi_y) / font->height; i++) | |
222 | early_efi_scroll_up(); | |
223 | ||
5f35eb0e DY |
224 | /* early_console_register will unset CON_BOOT in case ,keep */ |
225 | if (!(con->flags & CON_BOOT)) | |
226 | early_efi_keep = true; | |
72548e83 MF |
227 | return 0; |
228 | } | |
229 | ||
230 | struct console early_efi_console = { | |
231 | .name = "earlyefi", | |
232 | .write = early_efi_write, | |
233 | .setup = early_efi_setup, | |
234 | .flags = CON_PRINTBUFFER, | |
235 | .index = -1, | |
236 | }; |