1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2013 Intel Corporation; author Matt Fleming
6 #include <linux/console.h>
8 #include <linux/font.h>
10 #include <linux/kernel.h>
11 #include <linux/serial_core.h>
12 #include <linux/screen_info.h>
13 #include <linux/string.h>
15 #include <asm/early_ioremap.h>
17 static const struct console
*earlycon_console __initdata
;
18 static const struct font_desc
*font
;
19 static u16 cur_line_y
, max_line_y
;
20 static u32 efi_x_array
[1024];
21 static u32 efi_x
, efi_y
;
27 * EFI earlycon needs to use early_memremap() to map the framebuffer.
28 * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
29 * memremap() should be used instead. memremap() will be available after
30 * paging_init() which is earlier than initcall callbacks. Thus adding this
31 * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
33 static int __init
efi_earlycon_remap_fb(void)
35 /* bail if there is no bootconsole or it was unregistered already */
36 if (!earlycon_console
|| !console_is_registered(earlycon_console
))
39 efi_fb
= memremap(fb_base
, screen_info
.lfb_size
,
40 fb_wb
? MEMREMAP_WB
: MEMREMAP_WC
);
42 return efi_fb
? 0 : -ENOMEM
;
44 early_initcall(efi_earlycon_remap_fb
);
46 static int __init
efi_earlycon_unmap_fb(void)
48 /* unmap the bootconsole fb unless keep_bootcon left it registered */
49 if (efi_fb
&& !console_is_registered(earlycon_console
))
53 late_initcall(efi_earlycon_unmap_fb
);
55 static __ref
void *efi_earlycon_map(unsigned long start
, unsigned long len
)
60 return efi_fb
+ start
;
62 fb_prot
= fb_wb
? PAGE_KERNEL
: pgprot_writecombine(PAGE_KERNEL
);
63 return early_memremap_prot(fb_base
+ start
, len
, pgprot_val(fb_prot
));
66 static __ref
void efi_earlycon_unmap(void *addr
, unsigned long len
)
71 early_memunmap(addr
, len
);
74 static void efi_earlycon_clear_scanline(unsigned int y
)
79 len
= screen_info
.lfb_linelength
;
80 dst
= efi_earlycon_map(y
*len
, len
);
85 efi_earlycon_unmap(dst
, len
);
88 static void efi_earlycon_scroll_up(void)
90 unsigned long *dst
, *src
;
95 /* Find the cached maximum x coordinate */
96 for (i
= 0; i
< max_line_y
; i
++) {
97 if (efi_x_array
[i
] > maxlen
)
98 maxlen
= efi_x_array
[i
];
102 len
= screen_info
.lfb_linelength
;
103 height
= screen_info
.lfb_height
;
105 for (i
= 0; i
< height
- font
->height
; i
++) {
106 dst
= efi_earlycon_map(i
*len
, len
);
110 src
= efi_earlycon_map((i
+ font
->height
) * len
, len
);
112 efi_earlycon_unmap(dst
, len
);
116 memmove(dst
, src
, maxlen
);
118 efi_earlycon_unmap(src
, len
);
119 efi_earlycon_unmap(dst
, len
);
123 static void efi_earlycon_write_char(u32
*dst
, unsigned char c
, unsigned int h
)
125 const u32 color_black
= 0x00000000;
126 const u32 color_white
= 0x00ffffff;
131 bytes
= BITS_TO_BYTES(font
->width
);
132 src
= font
->data
+ c
* font
->height
* bytes
+ h
* bytes
;
134 for (m
= 0; m
< font
->width
; m
++) {
137 if ((x
>> (7 - n
)) & 1)
146 efi_earlycon_write(struct console
*con
, const char *str
, unsigned int num
)
148 struct screen_info
*si
;
149 u32 cur_efi_x
= efi_x
;
155 len
= si
->lfb_linelength
;
158 unsigned int linemax
= (si
->lfb_width
- efi_x
) / font
->width
;
159 unsigned int h
, count
;
161 count
= strnchrnul(str
, num
, '\n') - str
;
165 for (h
= 0; h
< font
->height
; h
++) {
168 dst
= efi_earlycon_map((efi_y
+ h
) * len
, len
);
177 efi_earlycon_write_char(dst
+ x
*4, *s
, h
);
182 efi_earlycon_unmap(dst
, len
);
186 efi_x
+= count
* font
->width
;
189 if (num
> 0 && *s
== '\n') {
192 efi_y
+= font
->height
;
197 if (efi_x
+ font
->width
> si
->lfb_width
) {
200 efi_y
+= font
->height
;
203 if (efi_y
+ font
->height
> si
->lfb_height
) {
206 efi_x_array
[cur_line_y
] = cur_efi_x
;
207 cur_line_y
= (cur_line_y
+ 1) % max_line_y
;
209 efi_y
-= font
->height
;
210 efi_earlycon_scroll_up();
212 for (i
= 0; i
< font
->height
; i
++)
213 efi_earlycon_clear_scanline(efi_y
+ i
);
218 static bool __initdata fb_probed
;
220 void __init
efi_earlycon_reprobe(void)
223 setup_earlycon("efifb");
226 static int __init
efi_earlycon_setup(struct earlycon_device
*device
,
229 struct screen_info
*si
;
233 fb_wb
= opt
&& !strcmp(opt
, "ram");
235 if (screen_info
.orig_video_isVGA
!= VIDEO_TYPE_EFI
) {
240 fb_base
= screen_info
.lfb_base
;
241 if (screen_info
.capabilities
& VIDEO_CAPABILITY_64BIT_BASE
)
242 fb_base
|= (u64
)screen_info
.ext_lfb_base
<< 32;
245 xres
= si
->lfb_width
;
246 yres
= si
->lfb_height
;
249 * efi_earlycon_write_char() implicitly assumes a framebuffer with
252 if (si
->lfb_depth
!= 32)
255 font
= get_default_font(xres
, yres
, NULL
, NULL
);
259 /* Fill the cache with maximum possible value of x coordinate */
260 memset32(efi_x_array
, rounddown(xres
, font
->width
), ARRAY_SIZE(efi_x_array
));
261 efi_y
= rounddown(yres
, font
->height
);
263 /* Make sure we have cache for the x coordinate for the full screen */
264 max_line_y
= efi_y
/ font
->height
+ 1;
267 efi_y
-= font
->height
;
268 for (i
= 0; i
< (yres
- efi_y
) / font
->height
; i
++)
269 efi_earlycon_scroll_up();
271 device
->con
->write
= efi_earlycon_write
;
272 earlycon_console
= device
->con
;
275 EARLYCON_DECLARE(efifb
, efi_earlycon_setup
);