linux/drivers/firmware/efi/earlycon.c
<<
>>
Prefs
   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
  16static const struct font_desc *font;
  17static u32 efi_x, efi_y;
  18static u64 fb_base;
  19static pgprot_t fb_prot;
  20
  21static __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
  26static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
  27{
  28        early_memunmap(addr, len);
  29}
  30
  31static 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
  45static 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
  72static 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
  92static void
  93efi_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
 165static 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}
 206EARLYCON_DECLARE(efifb, efi_earlycon_setup);
 207