root/drivers/firmware/efi/earlycon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. efi_earlycon_remap_fb
  2. efi_earlycon_unmap_fb
  3. efi_earlycon_map
  4. efi_earlycon_unmap
  5. efi_earlycon_clear_scanline
  6. efi_earlycon_scroll_up
  7. efi_earlycon_write_char
  8. efi_earlycon_write
  9. efi_earlycon_setup

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

/* [<][>][^][v][top][bottom][index][help] */