earlycon.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2013 Intel Corporation; author Matt Fleming
  4. */
  5. #include <linux/console.h>
  6. #include <linux/efi.h>
  7. #include <linux/font.h>
  8. #include <linux/io.h>
  9. #include <linux/kernel.h>
  10. #include <linux/serial_core.h>
  11. #include <linux/screen_info.h>
  12. #include <asm/early_ioremap.h>
  13. static const struct console *earlycon_console __initdata;
  14. static const struct font_desc *font;
  15. static u32 efi_x, efi_y;
  16. static u64 fb_base;
  17. static bool fb_wb;
  18. static void *efi_fb;
  19. /*
  20. * EFI earlycon needs to use early_memremap() to map the framebuffer.
  21. * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
  22. * memremap() should be used instead. memremap() will be available after
  23. * paging_init() which is earlier than initcall callbacks. Thus adding this
  24. * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
  25. */
  26. static int __init efi_earlycon_remap_fb(void)
  27. {
  28. /* bail if there is no bootconsole or it has been disabled already */
  29. if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
  30. return 0;
  31. efi_fb = memremap(fb_base, screen_info.lfb_size,
  32. fb_wb ? MEMREMAP_WB : MEMREMAP_WC);
  33. return efi_fb ? 0 : -ENOMEM;
  34. }
  35. early_initcall(efi_earlycon_remap_fb);
  36. static int __init efi_earlycon_unmap_fb(void)
  37. {
  38. /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
  39. if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
  40. memunmap(efi_fb);
  41. return 0;
  42. }
  43. late_initcall(efi_earlycon_unmap_fb);
  44. static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
  45. {
  46. pgprot_t fb_prot;
  47. if (efi_fb)
  48. return efi_fb + start;
  49. fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL);
  50. return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
  51. }
  52. static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
  53. {
  54. if (efi_fb)
  55. return;
  56. early_memunmap(addr, len);
  57. }
  58. static void efi_earlycon_clear_scanline(unsigned int y)
  59. {
  60. unsigned long *dst;
  61. u16 len;
  62. len = screen_info.lfb_linelength;
  63. dst = efi_earlycon_map(y*len, len);
  64. if (!dst)
  65. return;
  66. memset(dst, 0, len);
  67. efi_earlycon_unmap(dst, len);
  68. }
  69. static void efi_earlycon_scroll_up(void)
  70. {
  71. unsigned long *dst, *src;
  72. u16 len;
  73. u32 i, height;
  74. len = screen_info.lfb_linelength;
  75. height = screen_info.lfb_height;
  76. for (i = 0; i < height - font->height; i++) {
  77. dst = efi_earlycon_map(i*len, len);
  78. if (!dst)
  79. return;
  80. src = efi_earlycon_map((i + font->height) * len, len);
  81. if (!src) {
  82. efi_earlycon_unmap(dst, len);
  83. return;
  84. }
  85. memmove(dst, src, len);
  86. efi_earlycon_unmap(src, len);
  87. efi_earlycon_unmap(dst, len);
  88. }
  89. }
  90. static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
  91. {
  92. const u32 color_black = 0x00000000;
  93. const u32 color_white = 0x00ffffff;
  94. const u8 *src;
  95. int m, n, bytes;
  96. u8 x;
  97. bytes = BITS_TO_BYTES(font->width);
  98. src = font->data + c * font->height * bytes + h * bytes;
  99. for (m = 0; m < font->width; m++) {
  100. n = m % 8;
  101. x = *(src + m / 8);
  102. if ((x >> (7 - n)) & 1)
  103. *dst = color_white;
  104. else
  105. *dst = color_black;
  106. dst++;
  107. }
  108. }
  109. static void
  110. efi_earlycon_write(struct console *con, const char *str, unsigned int num)
  111. {
  112. struct screen_info *si;
  113. unsigned int len;
  114. const char *s;
  115. void *dst;
  116. si = &screen_info;
  117. len = si->lfb_linelength;
  118. while (num) {
  119. unsigned int linemax;
  120. unsigned int h, count = 0;
  121. for (s = str; *s && *s != '\n'; s++) {
  122. if (count == num)
  123. break;
  124. count++;
  125. }
  126. linemax = (si->lfb_width - efi_x) / font->width;
  127. if (count > linemax)
  128. count = linemax;
  129. for (h = 0; h < font->height; h++) {
  130. unsigned int n, x;
  131. dst = efi_earlycon_map((efi_y + h) * len, len);
  132. if (!dst)
  133. return;
  134. s = str;
  135. n = count;
  136. x = efi_x;
  137. while (n-- > 0) {
  138. efi_earlycon_write_char(dst + x*4, *s, h);
  139. x += font->width;
  140. s++;
  141. }
  142. efi_earlycon_unmap(dst, len);
  143. }
  144. num -= count;
  145. efi_x += count * font->width;
  146. str += count;
  147. if (num > 0 && *s == '\n') {
  148. efi_x = 0;
  149. efi_y += font->height;
  150. str++;
  151. num--;
  152. }
  153. if (efi_x + font->width > si->lfb_width) {
  154. efi_x = 0;
  155. efi_y += font->height;
  156. }
  157. if (efi_y + font->height > si->lfb_height) {
  158. u32 i;
  159. efi_y -= font->height;
  160. efi_earlycon_scroll_up();
  161. for (i = 0; i < font->height; i++)
  162. efi_earlycon_clear_scanline(efi_y + i);
  163. }
  164. }
  165. }
  166. static int __init efi_earlycon_setup(struct earlycon_device *device,
  167. const char *opt)
  168. {
  169. struct screen_info *si;
  170. u16 xres, yres;
  171. u32 i;
  172. if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
  173. return -ENODEV;
  174. fb_base = screen_info.lfb_base;
  175. if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
  176. fb_base |= (u64)screen_info.ext_lfb_base << 32;
  177. fb_wb = opt && !strcmp(opt, "ram");
  178. si = &screen_info;
  179. xres = si->lfb_width;
  180. yres = si->lfb_height;
  181. /*
  182. * efi_earlycon_write_char() implicitly assumes a framebuffer with
  183. * 32 bits per pixel.
  184. */
  185. if (si->lfb_depth != 32)
  186. return -ENODEV;
  187. font = get_default_font(xres, yres, -1, -1);
  188. if (!font)
  189. return -ENODEV;
  190. efi_y = rounddown(yres, font->height) - font->height;
  191. for (i = 0; i < (yres - efi_y) / font->height; i++)
  192. efi_earlycon_scroll_up();
  193. device->con->write = efi_earlycon_write;
  194. earlycon_console = device->con;
  195. return 0;
  196. }
  197. EARLYCON_DECLARE(efifb, efi_earlycon_setup);