Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright (C) 2013 Intel Corporation; author Matt Fleming
4 : : */
5 : :
6 : : #include <linux/console.h>
7 : : #include <linux/efi.h>
8 : : #include <linux/font.h>
9 : : #include <linux/io.h>
10 : : #include <linux/kernel.h>
11 : : #include <linux/serial_core.h>
12 : : #include <linux/screen_info.h>
13 : :
14 : : #include <asm/early_ioremap.h>
15 : :
16 : : static const struct console *earlycon_console __initdata;
17 : : static const struct font_desc *font;
18 : : static u32 efi_x, efi_y;
19 : : static u64 fb_base;
20 : : static bool fb_wb;
21 : : static void *efi_fb;
22 : :
23 : : /*
24 : : * EFI earlycon needs to use early_memremap() to map the framebuffer.
25 : : * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
26 : : * memremap() should be used instead. memremap() will be available after
27 : : * paging_init() which is earlier than initcall callbacks. Thus adding this
28 : : * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
29 : : */
30 : 28 : static int __init efi_earlycon_remap_fb(void)
31 : : {
32 : : /* bail if there is no bootconsole or it has been disabled already */
33 [ - + - - ]: 28 : if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
34 : : return 0;
35 : :
36 : 0 : efi_fb = memremap(fb_base, screen_info.lfb_size,
37 [ # # ]: 0 : fb_wb ? MEMREMAP_WB : MEMREMAP_WC);
38 : :
39 [ # # ]: 0 : return efi_fb ? 0 : -ENOMEM;
40 : : }
41 : : early_initcall(efi_earlycon_remap_fb);
42 : :
43 : 28 : static int __init efi_earlycon_unmap_fb(void)
44 : : {
45 : : /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
46 [ - + - - ]: 28 : if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
47 : 0 : memunmap(efi_fb);
48 : 28 : return 0;
49 : : }
50 : : late_initcall(efi_earlycon_unmap_fb);
51 : :
52 : 0 : static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
53 : : {
54 : 0 : pgprot_t fb_prot;
55 : :
56 [ # # ]: 0 : if (efi_fb)
57 : 0 : return efi_fb + start;
58 : :
59 [ # # ]: 0 : fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL);
60 : 0 : return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
61 : : }
62 : :
63 : 0 : static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
64 : : {
65 [ # # ]: 0 : if (efi_fb)
66 : : return;
67 : :
68 : 0 : early_memunmap(addr, len);
69 : : }
70 : :
71 : 0 : static void efi_earlycon_clear_scanline(unsigned int y)
72 : : {
73 : 0 : unsigned long *dst;
74 : 0 : u16 len;
75 : :
76 : 0 : len = screen_info.lfb_linelength;
77 : 0 : dst = efi_earlycon_map(y*len, len);
78 [ # # ]: 0 : if (!dst)
79 : : return;
80 : :
81 : 0 : memset(dst, 0, len);
82 : 0 : efi_earlycon_unmap(dst, len);
83 : : }
84 : :
85 : 0 : static void efi_earlycon_scroll_up(void)
86 : : {
87 : 0 : unsigned long *dst, *src;
88 : 0 : u16 len;
89 : 0 : u32 i, height;
90 : :
91 : 0 : len = screen_info.lfb_linelength;
92 : 0 : height = screen_info.lfb_height;
93 : :
94 [ # # ]: 0 : for (i = 0; i < height - font->height; i++) {
95 : 0 : dst = efi_earlycon_map(i*len, len);
96 [ # # ]: 0 : if (!dst)
97 : : return;
98 : :
99 : 0 : src = efi_earlycon_map((i + font->height) * len, len);
100 [ # # ]: 0 : if (!src) {
101 : 0 : efi_earlycon_unmap(dst, len);
102 : 0 : return;
103 : : }
104 : :
105 : 0 : memmove(dst, src, len);
106 : :
107 : 0 : efi_earlycon_unmap(src, len);
108 : 0 : efi_earlycon_unmap(dst, len);
109 : : }
110 : : }
111 : :
112 : 0 : static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
113 : : {
114 : 0 : const u32 color_black = 0x00000000;
115 : 0 : const u32 color_white = 0x00ffffff;
116 : 0 : const u8 *src;
117 : 0 : u8 s8;
118 : 0 : int m;
119 : :
120 : 0 : src = font->data + c * font->height;
121 : 0 : s8 = *(src + h);
122 : :
123 [ # # ]: 0 : for (m = 0; m < 8; m++) {
124 [ # # ]: 0 : if ((s8 >> (7 - m)) & 1)
125 : 0 : *dst = color_white;
126 : : else
127 : 0 : *dst = color_black;
128 : 0 : dst++;
129 : : }
130 : : }
131 : :
132 : : static void
133 : 0 : efi_earlycon_write(struct console *con, const char *str, unsigned int num)
134 : : {
135 : 0 : struct screen_info *si;
136 : 0 : unsigned int len;
137 : 0 : const char *s;
138 : 0 : void *dst;
139 : :
140 : 0 : si = &screen_info;
141 : 0 : len = si->lfb_linelength;
142 : :
143 [ # # ]: 0 : while (num) {
144 : : unsigned int linemax;
145 : : unsigned int h, count = 0;
146 : :
147 [ # # ]: 0 : for (s = str; *s && *s != '\n'; s++) {
148 [ # # ]: 0 : if (count == num)
149 : : break;
150 : 0 : count++;
151 : : }
152 : :
153 : 0 : linemax = (si->lfb_width - efi_x) / font->width;
154 : 0 : if (count > linemax)
155 : : count = linemax;
156 : :
157 [ # # ]: 0 : for (h = 0; h < font->height; h++) {
158 : 0 : unsigned int n, x;
159 : :
160 : 0 : dst = efi_earlycon_map((efi_y + h) * len, len);
161 [ # # ]: 0 : if (!dst)
162 : : return;
163 : :
164 : 0 : s = str;
165 : 0 : n = count;
166 : 0 : x = efi_x;
167 : :
168 [ # # ]: 0 : while (n-- > 0) {
169 : 0 : efi_earlycon_write_char(dst + x*4, *s, h);
170 : 0 : x += font->width;
171 : 0 : s++;
172 : : }
173 : :
174 : 0 : efi_earlycon_unmap(dst, len);
175 : : }
176 : :
177 : 0 : num -= count;
178 : 0 : efi_x += count * font->width;
179 : 0 : str += count;
180 : :
181 [ # # # # ]: 0 : if (num > 0 && *s == '\n') {
182 : 0 : efi_x = 0;
183 : 0 : efi_y += font->height;
184 : 0 : str++;
185 : 0 : num--;
186 : : }
187 : :
188 [ # # ]: 0 : if (efi_x + font->width > si->lfb_width) {
189 : 0 : efi_x = 0;
190 : 0 : efi_y += font->height;
191 : : }
192 : :
193 [ # # ]: 0 : if (efi_y + font->height > si->lfb_height) {
194 : 0 : u32 i;
195 : :
196 : 0 : efi_y -= font->height;
197 : 0 : efi_earlycon_scroll_up();
198 : :
199 [ # # ]: 0 : for (i = 0; i < font->height; i++)
200 : 0 : efi_earlycon_clear_scanline(efi_y + i);
201 : : }
202 : : }
203 : : }
204 : :
205 : 0 : static int __init efi_earlycon_setup(struct earlycon_device *device,
206 : : const char *opt)
207 : : {
208 : 0 : struct screen_info *si;
209 : 0 : u16 xres, yres;
210 : 0 : u32 i;
211 : :
212 [ # # ]: 0 : if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
213 : : return -ENODEV;
214 : :
215 : 0 : fb_base = screen_info.lfb_base;
216 [ # # ]: 0 : if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
217 : 0 : fb_base |= (u64)screen_info.ext_lfb_base << 32;
218 : :
219 [ # # # # ]: 0 : fb_wb = opt && !strcmp(opt, "ram");
220 : :
221 : 0 : si = &screen_info;
222 : 0 : xres = si->lfb_width;
223 : 0 : yres = si->lfb_height;
224 : :
225 : : /*
226 : : * efi_earlycon_write_char() implicitly assumes a framebuffer with
227 : : * 32 bits per pixel.
228 : : */
229 [ # # ]: 0 : if (si->lfb_depth != 32)
230 : : return -ENODEV;
231 : :
232 : 0 : font = get_default_font(xres, yres, -1, -1);
233 [ # # ]: 0 : if (!font)
234 : : return -ENODEV;
235 : :
236 : 0 : efi_y = rounddown(yres, font->height) - font->height;
237 [ # # ]: 0 : for (i = 0; i < (yres - efi_y) / font->height; i++)
238 : 0 : efi_earlycon_scroll_up();
239 : :
240 : 0 : device->con->write = efi_earlycon_write;
241 : 0 : earlycon_console = device->con;
242 : 0 : return 0;
243 : : }
244 : : EARLYCON_DECLARE(efifb, efi_earlycon_setup);
|