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
15static const struct font_desc *font;
16static u32 efi_x, efi_y;
17static void *efi_fb;
18static bool early_efi_keep;
19
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 */
27static __init int early_efi_map_fb(void)
28{
29	unsigned long base, size;
30
31	if (!early_efi_keep)
32		return 0;
33
34	base = boot_params.screen_info.lfb_base;
35	size = boot_params.screen_info.lfb_size;
36	efi_fb = ioremap(base, size);
37
38	return efi_fb ? 0 : -ENOMEM;
39}
40early_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 */
47static __init_refok void *early_efi_map(unsigned long start, unsigned long len)
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}
58
59static __init_refok void early_efi_unmap(void *addr, unsigned long len)
60{
61	if (!efi_fb)
62		early_iounmap(addr, len);
63}
64
65static 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);
72	if (!dst)
73		return;
74
75	memset(dst, 0, len);
76	early_efi_unmap(dst, len);
77}
78
79static void early_efi_scroll_up(void)
80{
81	unsigned long *dst, *src;
82	u16 len;
83	u32 i, height;
84
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++) {
89		dst = early_efi_map(i*len, len);
90		if (!dst)
91			return;
92
93		src = early_efi_map((i + font->height) * len, len);
94		if (!src) {
95			early_efi_unmap(dst, len);
96			return;
97		}
98
99		memmove(dst, src, len);
100
101		early_efi_unmap(src, len);
102		early_efi_unmap(dst, len);
103	}
104}
105
106static 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
126static void
127early_efi_write(struct console *con, const char *str, unsigned int num)
128{
129	struct screen_info *si;
130	unsigned int len;
131	const char *s;
132	void *dst;
133
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
154			dst = early_efi_map((efi_y + h) * len, len);
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
168			early_efi_unmap(dst, len);
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
187		if (efi_y + font->height > si->lfb_height) {
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
199static __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
224	/* early_console_register will unset CON_BOOT in case ,keep */
225	if (!(con->flags & CON_BOOT))
226		early_efi_keep = true;
227	return 0;
228}
229
230struct 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};
237