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 font_desc *font; 17 static u32 efi_x, efi_y; 18 static u64 fb_base; 19 static pgprot_t fb_prot; 20 21 static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) 22 { 23 return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); 24 } 25 26 static __ref void efi_earlycon_unmap(void *addr, unsigned long len) 27 { 28 early_memunmap(addr, len); 29 } 30 31 static void efi_earlycon_clear_scanline(unsigned int y) 32 { 33 unsigned long *dst; 34 u16 len; 35 36 len = screen_info.lfb_linelength; 37 dst = efi_earlycon_map(y*len, len); 38 if (!dst) 39 return; 40 41 memset(dst, 0, len); 42 efi_earlycon_unmap(dst, len); 43 } 44 45 static void efi_earlycon_scroll_up(void) 46 { 47 unsigned long *dst, *src; 48 u16 len; 49 u32 i, height; 50 51 len = screen_info.lfb_linelength; 52 height = screen_info.lfb_height; 53 54 for (i = 0; i < height - font->height; i++) { 55 dst = efi_earlycon_map(i*len, len); 56 if (!dst) 57 return; 58 59 src = efi_earlycon_map((i + font->height) * len, len); 60 if (!src) { 61 efi_earlycon_unmap(dst, len); 62 return; 63 } 64 65 memmove(dst, src, len); 66 67 efi_earlycon_unmap(src, len); 68 efi_earlycon_unmap(dst, len); 69 } 70 } 71 72 static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) 73 { 74 const u32 color_black = 0x00000000; 75 const u32 color_white = 0x00ffffff; 76 const u8 *src; 77 u8 s8; 78 int m; 79 80 src = font->data + c * font->height; 81 s8 = *(src + h); 82 83 for (m = 0; m < 8; m++) { 84 if ((s8 >> (7 - m)) & 1) 85 *dst = color_white; 86 else 87 *dst = color_black; 88 dst++; 89 } 90 } 91 92 static void 93 efi_earlycon_write(struct console *con, const char *str, unsigned int num) 94 { 95 struct screen_info *si; 96 unsigned int len; 97 const char *s; 98 void *dst; 99 100 si = &screen_info; 101 len = si->lfb_linelength; 102 103 while (num) { 104 unsigned int linemax; 105 unsigned int h, count = 0; 106 107 for (s = str; *s && *s != '\n'; s++) { 108 if (count == num) 109 break; 110 count++; 111 } 112 113 linemax = (si->lfb_width - efi_x) / font->width; 114 if (count > linemax) 115 count = linemax; 116 117 for (h = 0; h < font->height; h++) { 118 unsigned int n, x; 119 120 dst = efi_earlycon_map((efi_y + h) * len, len); 121 if (!dst) 122 return; 123 124 s = str; 125 n = count; 126 x = efi_x; 127 128 while (n-- > 0) { 129 efi_earlycon_write_char(dst + x*4, *s, h); 130 x += font->width; 131 s++; 132 } 133 134 efi_earlycon_unmap(dst, len); 135 } 136 137 num -= count; 138 efi_x += count * font->width; 139 str += count; 140 141 if (num > 0 && *s == '\n') { 142 efi_x = 0; 143 efi_y += font->height; 144 str++; 145 num--; 146 } 147 148 if (efi_x + font->width > si->lfb_width) { 149 efi_x = 0; 150 efi_y += font->height; 151 } 152 153 if (efi_y + font->height > si->lfb_height) { 154 u32 i; 155 156 efi_y -= font->height; 157 efi_earlycon_scroll_up(); 158 159 for (i = 0; i < font->height; i++) 160 efi_earlycon_clear_scanline(efi_y + i); 161 } 162 } 163 } 164 165 static int __init efi_earlycon_setup(struct earlycon_device *device, 166 const char *opt) 167 { 168 struct screen_info *si; 169 u16 xres, yres; 170 u32 i; 171 172 if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) 173 return -ENODEV; 174 175 fb_base = screen_info.lfb_base; 176 if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) 177 fb_base |= (u64)screen_info.ext_lfb_base << 32; 178 179 if (opt && !strcmp(opt, "ram")) 180 fb_prot = PAGE_KERNEL; 181 else 182 fb_prot = pgprot_writecombine(PAGE_KERNEL); 183 184 si = &screen_info; 185 xres = si->lfb_width; 186 yres = si->lfb_height; 187 188 /* 189 * efi_earlycon_write_char() implicitly assumes a framebuffer with 190 * 32 bits per pixel. 191 */ 192 if (si->lfb_depth != 32) 193 return -ENODEV; 194 195 font = get_default_font(xres, yres, -1, -1); 196 if (!font) 197 return -ENODEV; 198 199 efi_y = rounddown(yres, font->height) - font->height; 200 for (i = 0; i < (yres - efi_y) / font->height; i++) 201 efi_earlycon_scroll_up(); 202 203 device->con->write = efi_earlycon_write; 204 return 0; 205 } 206 EARLYCON_DECLARE(efifb, efi_earlycon_setup); 207