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