LCOV - code coverage report
Current view: top level - drivers/firmware/efi - earlycon.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 5 118 4.2 %
Date: 2022-04-01 14:58:12 Functions: 2 8 25.0 %
Branches: 2 66 3.0 %

           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                 :          3 : static int __init efi_earlycon_remap_fb(void)
      31                 :            : {
      32                 :            :         /* bail if there is no bootconsole or it has been disabled already */
      33   [ -  +  -  - ]:          3 :         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                 :          3 : static int __init efi_earlycon_unmap_fb(void)
      44                 :            : {
      45                 :            :         /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
      46   [ -  +  -  - ]:          3 :         if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
      47                 :          0 :                 memunmap(efi_fb);
      48                 :          3 :         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);

Generated by: LCOV version 1.14