linux/arch/x86/platform/efi/early_printk.c
<<
>>
Prefs
   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