Message ID | 20220806163255.10404-4-markuss.broks@gmail.com |
---|---|
State | New |
Headers | show |
Series | Add generic framebuffer support to EFI earlycon driver | expand |
On Sat, Aug 6, 2022 at 6:38 PM Markuss Broks <markuss.broks@gmail.com> wrote: > > Add early console support for generic linear framebuffer devices. > This driver supports probing from cmdline early parameters > or from the device-tree using information in simple-framebuffer node. > The EFI functionality should be retained in whole. > The driver was disabled on ARM because of a bug in early_ioremap We refer to functions like func(). > implementation on ARM and on IA64 because of lack of early_memremap_prot. Ditto. ... > +#include <asm/early_ioremap.h> Can it be placed after linux/* ones? > +#include <linux/console.h> > +#include <linux/efi.h> > +#include <linux/font.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/mm.h> > +#include <linux/of.h> > +#include <linux/of_fdt.h> > +#include <linux/serial_core.h> > +#include <linux/screen_info.h> ... > +static int __init simplefb_earlycon_remap_fb(void) > +{ > + unsigned long mapping; + Blank line. > + /* bail if there is no bootconsole or it has been disabled already */ > + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) > + return 0; > + > + if (region_intersects(info.phys_base, info.size, > + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS) > + mapping = MEMREMAP_WB; > + else > + mapping = MEMREMAP_WC; > + info.virt_base = memremap(info.phys_base, info.size, mapping); > + > + return info.virt_base ? 0 : -ENOMEM; Easier to read the standard pattern: if (!info.virt_base) return -ENOMEM; return 0; > +} ... > +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h) > +{ > + const u8 *src; > + int m, n, bytes; > + u8 x; > + > + bytes = BITS_TO_BYTES(font->width); > + src = font->data + c * font->height * bytes + h * bytes; > + > + for (m = 0; m < font->width; m++) { > + n = m % 8; > + x = *(src + m / 8); I would write it as x = src[m / 8]; > + if ((x >> (7 - n)) & 1) > + memset(dst, 0xff, (info.depth / 8)); Too many parentheses. > + else > + memset(dst, 0, (info.depth / 8)); Ditto. > + dst += (info.depth / 8); Ditto. > + } > +}
Hi Markuss, I love your patch! Yet something to improve: [auto build test ERROR on tty/tty-testing] [also build test ERROR on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220805] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646 base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20220807/202208072145.yWoGkrW1-lkp@intel.com/config) compiler: sh4-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646 git checkout 97dfc2aa69b065de769a191352afe2099c52fedb # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): >> drivers/video/console/earlycon.c:7:10: fatal error: asm/early_ioremap.h: No such file or directory 7 | #include <asm/early_ioremap.h> | ^~~~~~~~~~~~~~~~~~~~~ compilation terminated. vim +7 drivers/video/console/earlycon.c > 7 #include <asm/early_ioremap.h> 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 <linux/mm.h> 14 #include <linux/of.h> 15 #include <linux/of_fdt.h> 16 #include <linux/serial_core.h> 17 #include <linux/screen_info.h> 18
Hi Markuss, I love your patch! Perhaps something to improve: [auto build test WARNING on tty/tty-testing] [also build test WARNING on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220812] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646 base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing config: loongarch-randconfig-s031-20220807 (https://download.01.org/0day-ci/archive/20220814/202208140705.bU9i1c1t-lkp@intel.com/config) compiler: loongarch64-linux-gcc (GCC) 12.1.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # apt-get install sparse # sparse version: v0.6.4-39-gce1a6720-dirty # https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646 git checkout 97dfc2aa69b065de769a191352afe2099c52fedb # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=loongarch SHELL=/bin/bash drivers/video/console/ If you fix the issue, kindly add following tag where applicable Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> drivers/video/console/earlycon.c:43:24: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __iomem *static [toplevel] virt_base @@ got void * @@ drivers/video/console/earlycon.c:43:24: sparse: expected void [noderef] __iomem *static [toplevel] virt_base drivers/video/console/earlycon.c:43:24: sparse: got void * >> drivers/video/console/earlycon.c:53:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void *addr @@ got void [noderef] __iomem *static [toplevel] virt_base @@ drivers/video/console/earlycon.c:53:30: sparse: expected void *addr drivers/video/console/earlycon.c:53:30: sparse: got void [noderef] __iomem *static [toplevel] virt_base >> drivers/video/console/earlycon.c:63:39: sparse: sparse: incorrect type in return expression (different address spaces) @@ expected void * @@ got void [noderef] __iomem * @@ drivers/video/console/earlycon.c:63:39: sparse: expected void * drivers/video/console/earlycon.c:63:39: sparse: got void [noderef] __iomem * >> drivers/video/console/earlycon.c:74:24: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __iomem *addr @@ got void *addr @@ drivers/video/console/earlycon.c:74:24: sparse: expected void [noderef] __iomem *addr drivers/video/console/earlycon.c:74:24: sparse: got void *addr vim +43 drivers/video/console/earlycon.c 29 30 static int __init simplefb_earlycon_remap_fb(void) 31 { 32 unsigned long mapping; 33 /* bail if there is no bootconsole or it has been disabled already */ 34 if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) 35 return 0; 36 37 if (region_intersects(info.phys_base, info.size, 38 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS) 39 mapping = MEMREMAP_WB; 40 else 41 mapping = MEMREMAP_WC; 42 > 43 info.virt_base = memremap(info.phys_base, info.size, mapping); 44 45 return info.virt_base ? 0 : -ENOMEM; 46 } 47 early_initcall(simplefb_earlycon_remap_fb); 48 49 static int __init simplefb_earlycon_unmap_fb(void) 50 { 51 /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ 52 if (info.virt_base && !(earlycon_console->flags & CON_ENABLED)) > 53 memunmap(info.virt_base); 54 return 0; 55 } 56 late_initcall(simplefb_earlycon_unmap_fb); 57 58 static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len) 59 { 60 pgprot_t fb_prot; 61 62 if (info.virt_base) > 63 return info.virt_base + start; 64 65 fb_prot = PAGE_KERNEL; 66 return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot)); 67 } 68 69 static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len) 70 { 71 if (info.virt_base) 72 return; 73 > 74 early_memunmap(addr, len); 75 } 76
Hi Markuss,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220812]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
config: x86_64-kismet-CONFIG_FB_EARLYCON-CONFIG_EFI_EARLYCON-0-0 (https://download.01.org/0day-ci/archive/20220814/202208141430.Ez8EkEwO-lkp@intel.com/config)
reproduce:
# https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
git checkout 97dfc2aa69b065de769a191352afe2099c52fedb
# 1. reproduce by kismet
# install kmax per https://github.com/paulgazz/kmax/blob/master/README.md
kismet --linux-ksrc=linux --selectees CONFIG_FB_EARLYCON --selectors CONFIG_EFI_EARLYCON -a=x86_64
# 2. reproduce by make
# save the config file to linux source tree
cd linux
make ARCH=x86_64 olddefconfig
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
kismet warnings: (new ones prefixed by >>)
>> kismet: WARNING: unmet direct dependencies detected for FB_EARLYCON when selected by EFI_EARLYCON
WARNING: unmet direct dependencies detected for FB_EARLYCON
Depends on [n]: VT [=y] && SERIAL_EARLYCON [=n] && !ARM && !IA64
Selected by [y]:
- EFI_EARLYCON [=y] && EFI [=y]
On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote: > Add early console support for generic linear framebuffer devices. > This driver supports probing from cmdline early parameters > or from the device-tree using information in simple-framebuffer node. > The EFI functionality should be retained in whole. > The driver was disabled on ARM because of a bug in early_ioremap > implementation on ARM and on IA64 because of lack of early_memremap_prot. > > Signed-off-by: Markuss Broks <markuss.broks@gmail.com> > --- > .../admin-guide/kernel-parameters.txt | 12 +- > MAINTAINERS | 5 + > drivers/firmware/efi/Kconfig | 7 +- > drivers/firmware/efi/Makefile | 1 - > drivers/firmware/efi/earlycon.c | 246 -------------- > drivers/video/console/Kconfig | 11 + > drivers/video/console/Makefile | 1 + > drivers/video/console/earlycon.c | 305 ++++++++++++++++++ Ok I have a more fundamental issue with this than the lack of proper patch splitting I mentioned in the other thread. This is the wrong place. drivers/video/console is about the various vt console implementations, which supply a struct consw to con_register_driver. This otoh is an (early) kernel/printk console implemented using struct console. Totally different thing, and really shouldn't end up in drivers/video/console imo. Somewhere in drivers/firmware might still be the best place, the sysfb stuff is also there. Maybe drivers/firmware/sysfb_earlycon.c? Also patch split is still an issue here, like I and Greg already said. -Daniel > 8 files changed, 332 insertions(+), 256 deletions(-) > delete mode 100644 drivers/firmware/efi/earlycon.c > create mode 100644 drivers/video/console/earlycon.c > > diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt > index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644 > --- a/Documentation/admin-guide/kernel-parameters.txt > +++ b/Documentation/admin-guide/kernel-parameters.txt > @@ -1281,12 +1281,9 @@ > specified address. The serial port must already be > setup and configured. Options are not yet supported. > > - efifb,[options] > + efifb > Start an early, unaccelerated console on the EFI > - memory mapped framebuffer (if available). On cache > - coherent non-x86 systems that use system memory for > - the framebuffer, pass the 'ram' option so that it is > - mapped with the correct attributes. > + memory mapped framebuffer (if available). > > linflex,<addr> > Use early console provided by Freescale LINFlexD UART > @@ -1294,6 +1291,11 @@ > address must be provided, and the serial port must > already be setup and configured. > > + simplefb,<addr>,<width>x<height>x<bpp> > + Use early console with simple framebuffer that is > + pre-initialized by firmware. A valid base address, > + width, height and pixel size must be provided. > + > earlyprintk= [X86,SH,ARM,M68k,S390] > earlyprintk=vga > earlyprintk=sclp > diff --git a/MAINTAINERS b/MAINTAINERS > index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ > T: git git://linuxtv.org/anttip/media_tree.git > F: drivers/media/tuners/e4000* > > +EARLY CONSOLE FRAMEBUFFER DRIVER > +M: Markuss Broks <markuss.broks@gmail.com> > +S: Maintained > +F: drivers/video/console/earlycon.c > + > EARTH_PT1 MEDIA DRIVER > M: Akihiro Tsukada <tskd08@gmail.com> > L: linux-media@vger.kernel.org > diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig > index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644 > --- a/drivers/firmware/efi/Kconfig > +++ b/drivers/firmware/efi/Kconfig > @@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA > may be used to override this option. > > config EFI_EARLYCON > - def_bool y > - depends on SERIAL_EARLYCON && !ARM && !IA64 > - select FONT_SUPPORT > - select ARCH_USE_MEMREMAP_PROT > + bool "EFI early console support" > + select FB_EARLYCON > + default y > > config EFI_CUSTOM_SSDT_OVERLAYS > bool "Load custom ACPI SSDT overlay from an EFI variable" > diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile > index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644 > --- a/drivers/firmware/efi/Makefile > +++ b/drivers/firmware/efi/Makefile > @@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y) > riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o > obj-$(CONFIG_RISCV) += $(riscv-obj-y) > obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o > -obj-$(CONFIG_EFI_EARLYCON) += earlycon.o > obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o > obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o > diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c > deleted file mode 100644 > index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000 > --- a/drivers/firmware/efi/earlycon.c > +++ /dev/null > @@ -1,246 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0 > -/* > - * Copyright (C) 2013 Intel Corporation; author Matt Fleming > - */ > - > -#include <linux/console.h> > -#include <linux/efi.h> > -#include <linux/font.h> > -#include <linux/io.h> > -#include <linux/kernel.h> > -#include <linux/serial_core.h> > -#include <linux/screen_info.h> > - > -#include <asm/early_ioremap.h> > - > -static const struct console *earlycon_console __initdata; > -static const struct font_desc *font; > -static u32 efi_x, efi_y; > -static u64 fb_base; > -static bool fb_wb; > -static void *efi_fb; > - > -/* > - * EFI earlycon needs to use early_memremap() to map the framebuffer. > - * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon', > - * memremap() should be used instead. memremap() will be available after > - * paging_init() which is earlier than initcall callbacks. Thus adding this > - * early initcall function early_efi_map_fb() to map the whole EFI framebuffer. > - */ > -static int __init efi_earlycon_remap_fb(void) > -{ > - /* bail if there is no bootconsole or it has been disabled already */ > - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) > - return 0; > - > - efi_fb = memremap(fb_base, screen_info.lfb_size, > - fb_wb ? MEMREMAP_WB : MEMREMAP_WC); > - > - return efi_fb ? 0 : -ENOMEM; > -} > -early_initcall(efi_earlycon_remap_fb); > - > -static int __init efi_earlycon_unmap_fb(void) > -{ > - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ > - if (efi_fb && !(earlycon_console->flags & CON_ENABLED)) > - memunmap(efi_fb); > - return 0; > -} > -late_initcall(efi_earlycon_unmap_fb); > - > -static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) > -{ > - pgprot_t fb_prot; > - > - if (efi_fb) > - return efi_fb + start; > - > - fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL); > - return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); > -} > - > -static __ref void efi_earlycon_unmap(void *addr, unsigned long len) > -{ > - if (efi_fb) > - return; > - > - early_memunmap(addr, len); > -} > - > -static void efi_earlycon_clear_scanline(unsigned int y) > -{ > - unsigned long *dst; > - u16 len; > - > - len = screen_info.lfb_linelength; > - dst = efi_earlycon_map(y*len, len); > - if (!dst) > - return; > - > - memset(dst, 0, len); > - efi_earlycon_unmap(dst, len); > -} > - > -static void efi_earlycon_scroll_up(void) > -{ > - unsigned long *dst, *src; > - u16 len; > - u32 i, height; > - > - len = screen_info.lfb_linelength; > - height = screen_info.lfb_height; > - > - for (i = 0; i < height - font->height; i++) { > - dst = efi_earlycon_map(i*len, len); > - if (!dst) > - return; > - > - src = efi_earlycon_map((i + font->height) * len, len); > - if (!src) { > - efi_earlycon_unmap(dst, len); > - return; > - } > - > - memmove(dst, src, len); > - > - efi_earlycon_unmap(src, len); > - efi_earlycon_unmap(dst, len); > - } > -} > - > -static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) > -{ > - const u32 color_black = 0x00000000; > - const u32 color_white = 0x00ffffff; > - const u8 *src; > - int m, n, bytes; > - u8 x; > - > - bytes = BITS_TO_BYTES(font->width); > - src = font->data + c * font->height * bytes + h * bytes; > - > - for (m = 0; m < font->width; m++) { > - n = m % 8; > - x = *(src + m / 8); > - if ((x >> (7 - n)) & 1) > - *dst = color_white; > - else > - *dst = color_black; > - dst++; > - } > -} > - > -static void > -efi_earlycon_write(struct console *con, const char *str, unsigned int num) > -{ > - struct screen_info *si; > - unsigned int len; > - const char *s; > - void *dst; > - > - si = &screen_info; > - len = si->lfb_linelength; > - > - while (num) { > - unsigned int linemax; > - unsigned int h, count = 0; > - > - for (s = str; *s && *s != '\n'; s++) { > - if (count == num) > - break; > - count++; > - } > - > - linemax = (si->lfb_width - efi_x) / font->width; > - if (count > linemax) > - count = linemax; > - > - for (h = 0; h < font->height; h++) { > - unsigned int n, x; > - > - dst = efi_earlycon_map((efi_y + h) * len, len); > - if (!dst) > - return; > - > - s = str; > - n = count; > - x = efi_x; > - > - while (n-- > 0) { > - efi_earlycon_write_char(dst + x*4, *s, h); > - x += font->width; > - s++; > - } > - > - efi_earlycon_unmap(dst, len); > - } > - > - num -= count; > - efi_x += count * font->width; > - str += count; > - > - if (num > 0 && *s == '\n') { > - efi_x = 0; > - efi_y += font->height; > - str++; > - num--; > - } > - > - if (efi_x + font->width > si->lfb_width) { > - efi_x = 0; > - efi_y += font->height; > - } > - > - if (efi_y + font->height > si->lfb_height) { > - u32 i; > - > - efi_y -= font->height; > - efi_earlycon_scroll_up(); > - > - for (i = 0; i < font->height; i++) > - efi_earlycon_clear_scanline(efi_y + i); > - } > - } > -} > - > -static int __init efi_earlycon_setup(struct earlycon_device *device, > - const char *opt) > -{ > - struct screen_info *si; > - u16 xres, yres; > - u32 i; > - > - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) > - return -ENODEV; > - > - fb_base = screen_info.lfb_base; > - if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) > - fb_base |= (u64)screen_info.ext_lfb_base << 32; > - > - fb_wb = opt && !strcmp(opt, "ram"); > - > - si = &screen_info; > - xres = si->lfb_width; > - yres = si->lfb_height; > - > - /* > - * efi_earlycon_write_char() implicitly assumes a framebuffer with > - * 32 bits per pixel. > - */ > - if (si->lfb_depth != 32) > - return -ENODEV; > - > - font = get_default_font(xres, yres, -1, -1); > - if (!font) > - return -ENODEV; > - > - efi_y = rounddown(yres, font->height) - font->height; > - for (i = 0; i < (yres - efi_y) / font->height; i++) > - efi_earlycon_scroll_up(); > - > - device->con->write = efi_earlycon_write; > - earlycon_console = device->con; > - return 0; > -} > -EARLYCON_DECLARE(efifb, efi_earlycon_setup); > diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig > index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644 > --- a/drivers/video/console/Kconfig > +++ b/drivers/video/console/Kconfig > @@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS > monitor. > Select 25 if you use a 640x480 resolution by default. > > +config FB_EARLYCON > + bool "Generic framebuffer early console" > + depends on SERIAL_EARLYCON && !ARM && !IA64 > + select FONT_SUPPORT > + select ARCH_USE_MEMREMAP_PROT > + help > + Say Y here if you want early console support for firmware established > + linear framebuffer. Unless you are using EFI framebuffer, you need to > + specify framebuffer geometry and address in device-tree or in kernel > + command line. > + > config FRAMEBUFFER_CONSOLE > bool "Framebuffer Console support" > depends on FB && !UML > diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile > index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644 > --- a/drivers/video/console/Makefile > +++ b/drivers/video/console/Makefile > @@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o > obj-$(CONFIG_VGA_CONSOLE) += vgacon.o > obj-$(CONFIG_MDA_CONSOLE) += mdacon.o > > +obj-$(CONFIG_FB_EARLYCON) += earlycon.o > obj-$(CONFIG_FB_STI) += sticore.o > diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c > new file mode 100644 > index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5 > --- /dev/null > +++ b/drivers/video/console/earlycon.c > @@ -0,0 +1,305 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2013 Intel Corporation; author Matt Fleming > + * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com> > + */ > + > +#include <asm/early_ioremap.h> > +#include <linux/console.h> > +#include <linux/efi.h> > +#include <linux/font.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/mm.h> > +#include <linux/of.h> > +#include <linux/of_fdt.h> > +#include <linux/serial_core.h> > +#include <linux/screen_info.h> > + > +struct fb_earlycon { > + u32 x, y, curr_x, curr_y, depth, stride; > + size_t size; > + phys_addr_t phys_base; > + void __iomem *virt_base; > +}; > + > +static const struct console *earlycon_console __initconst; > +static struct fb_earlycon info; > +static const struct font_desc *font; > + > +static int __init simplefb_earlycon_remap_fb(void) > +{ > + unsigned long mapping; > + /* bail if there is no bootconsole or it has been disabled already */ > + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) > + return 0; > + > + if (region_intersects(info.phys_base, info.size, > + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS) > + mapping = MEMREMAP_WB; > + else > + mapping = MEMREMAP_WC; > + > + info.virt_base = memremap(info.phys_base, info.size, mapping); > + > + return info.virt_base ? 0 : -ENOMEM; > +} > +early_initcall(simplefb_earlycon_remap_fb); > + > +static int __init simplefb_earlycon_unmap_fb(void) > +{ > + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ > + if (info.virt_base && !(earlycon_console->flags & CON_ENABLED)) > + memunmap(info.virt_base); > + return 0; > +} > +late_initcall(simplefb_earlycon_unmap_fb); > + > +static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len) > +{ > + pgprot_t fb_prot; > + > + if (info.virt_base) > + return info.virt_base + start; > + > + fb_prot = PAGE_KERNEL; > + return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot)); > +} > + > +static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len) > +{ > + if (info.virt_base) > + return; > + > + early_memunmap(addr, len); > +} > + > +static void simplefb_earlycon_clear_scanline(unsigned int y) > +{ > + unsigned long *dst; > + u16 len; > + > + len = info.stride; > + dst = simplefb_earlycon_map(y * len, len); > + if (!dst) > + return; > + > + memset(dst, 0, len); > + simplefb_earlycon_unmap(dst, len); > +} > + > +static void simplefb_earlycon_scroll_up(void) > +{ > + unsigned long *dst, *src; > + u16 len; > + u32 i, height; > + > + len = info.stride; > + height = info.y; > + > + for (i = 0; i < height - font->height; i++) { > + dst = simplefb_earlycon_map(i * len, len); > + if (!dst) > + return; > + > + src = simplefb_earlycon_map((i + font->height) * len, len); > + if (!src) { > + simplefb_earlycon_unmap(dst, len); > + return; > + } > + > + memmove(dst, src, len); > + > + simplefb_earlycon_unmap(src, len); > + simplefb_earlycon_unmap(dst, len); > + } > +} > + > +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h) > +{ > + const u8 *src; > + int m, n, bytes; > + u8 x; > + > + bytes = BITS_TO_BYTES(font->width); > + src = font->data + c * font->height * bytes + h * bytes; > + > + for (m = 0; m < font->width; m++) { > + n = m % 8; > + x = *(src + m / 8); > + if ((x >> (7 - n)) & 1) > + memset(dst, 0xff, (info.depth / 8)); > + else > + memset(dst, 0, (info.depth / 8)); > + dst += (info.depth / 8); > + } > +} > + > +static void > +simplefb_earlycon_write(struct console *con, const char *str, unsigned int num) > +{ > + unsigned int len; > + const char *s; > + void *dst; > + > + len = info.stride; > + > + while (num) { > + unsigned int linemax; > + unsigned int h, count = 0; > + > + for (s = str; *s && *s != '\n'; s++) { > + if (count == num) > + break; > + count++; > + } > + > + linemax = (info.x - info.curr_x) / font->width; > + if (count > linemax) > + count = linemax; > + > + for (h = 0; h < font->height; h++) { > + unsigned int n, x; > + > + dst = simplefb_earlycon_map((info.curr_y + h) * len, len); > + if (!dst) > + return; > + > + s = str; > + n = count; > + x = info.curr_x; > + > + while (n-- > 0) { > + simplefb_earlycon_write_char(dst + (x * 4), *s, h); > + x += font->width; > + s++; > + } > + > + simplefb_earlycon_unmap(dst, len); > + } > + > + num -= count; > + info.curr_x += count * font->width; > + str += count; > + > + if (num > 0 && *s == '\n') { > + info.curr_x = 0; > + info.curr_y += font->height; > + str++; > + num--; > + } > + > + if (info.curr_x + font->width > info.x) { > + info.curr_x = 0; > + info.curr_y += font->height; > + } > + > + if (info.curr_y + font->height > info.y) { > + u32 i; > + > + info.curr_y -= font->height; > + simplefb_earlycon_scroll_up(); > + > + for (i = 0; i < font->height; i++) > + simplefb_earlycon_clear_scanline(info.curr_y + i); > + } > + } > +} > + > +static int __init simplefb_earlycon_setup_common(struct earlycon_device *device, > + const char *opt) > +{ > + int i; > + > + info.size = info.x * info.y * (info.depth / 8); > + > + font = get_default_font(info.x, info.y, -1, -1); > + if (!font) > + return -ENODEV; > + > + info.curr_y = rounddown(info.y, font->height) - font->height; > + for (i = 0; i < (info.y - info.curr_y) / font->height; i++) > + simplefb_earlycon_scroll_up(); > + > + device->con->write = simplefb_earlycon_write; > + earlycon_console = device->con; > + return 0; > +} > + > +static int __init simplefb_earlycon_setup(struct earlycon_device *device, > + const char *opt) > +{ > + struct uart_port *port = &device->port; > + int ret; > + > + if (!port->mapbase) > + return -ENODEV; > + > + info.phys_base = port->mapbase; > + > + ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth); > + if (ret != 3) > + return -ENODEV; > + > + info.stride = info.x * (info.depth / 8); > + > + return simplefb_earlycon_setup_common(device, opt); > +} > + > +EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup); > + > +#ifdef CONFIG_EFI_EARLYCON > +static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device, > + const char *opt) > +{ > + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) > + return -ENODEV; > + > + info.phys_base = screen_info.lfb_base; > + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) > + info.phys_base |= (u64)screen_info.ext_lfb_base << 32; > + > + info.x = screen_info.lfb_width; > + info.y = screen_info.lfb_height; > + info.depth = screen_info.lfb_depth; > + info.stride = screen_info.lfb_linelength; > + > + return simplefb_earlycon_setup_common(device, opt); > +} > + > +EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi); > +#endif > + > +#ifdef CONFIG_OF_EARLY_FLATTREE > +static int __init simplefb_earlycon_setup_of(struct earlycon_device *device, > + const char *opt) > +{ > + struct uart_port *port = &device->port; > + const __be32 *val; > + > + if (!port->mapbase) > + return -ENODEV; > + > + info.phys_base = port->mapbase; > + > + val = of_get_flat_dt_prop(device->offset, "width", NULL); > + if (!val) > + return -ENODEV; > + info.x = be32_to_cpu(*val); > + > + val = of_get_flat_dt_prop(device->offset, "height", NULL); > + if (!val) > + return -ENODEV; > + info.y = be32_to_cpu(*val); > + > + val = of_get_flat_dt_prop(device->offset, "stride", NULL); > + if (!val) > + return -ENODEV; > + info.stride = be32_to_cpu(*val); > + info.depth = (info.stride / info.x) * 8; > + > + return simplefb_earlycon_setup_common(device, opt); > +} > + > +OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of); > +#endif > -- > 2.37.0 >
Hi Am 06.09.22 um 23:52 schrieb Daniel Vetter: > On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote: >> Add early console support for generic linear framebuffer devices. >> This driver supports probing from cmdline early parameters >> or from the device-tree using information in simple-framebuffer node. >> The EFI functionality should be retained in whole. >> The driver was disabled on ARM because of a bug in early_ioremap >> implementation on ARM and on IA64 because of lack of early_memremap_prot. >> >> Signed-off-by: Markuss Broks <markuss.broks@gmail.com> >> --- >> .../admin-guide/kernel-parameters.txt | 12 +- >> MAINTAINERS | 5 + >> drivers/firmware/efi/Kconfig | 7 +- >> drivers/firmware/efi/Makefile | 1 - >> drivers/firmware/efi/earlycon.c | 246 -------------- >> drivers/video/console/Kconfig | 11 + >> drivers/video/console/Makefile | 1 + >> drivers/video/console/earlycon.c | 305 ++++++++++++++++++ > > Ok I have a more fundamental issue with this than the lack of proper patch > splitting I mentioned in the other thread. > > This is the wrong place. > > drivers/video/console is about the various vt console implementations, > which supply a struct consw to con_register_driver. > > This otoh is an (early) kernel/printk console implemented using struct > console. Totally different thing, and really shouldn't end up in > drivers/video/console imo. Somewhere in drivers/firmware might still be > the best place, the sysfb stuff is also there. Maybe > drivers/firmware/sysfb_earlycon.c? Then better use just earlycon.c in that directory. We use the sysfb_ prefix for code of the framebuffer devices that simpledrm and simplefb run on top. It would not fit well here. Best regards Thomas > > Also patch split is still an issue here, like I and Greg already said. > -Daniel > >> 8 files changed, 332 insertions(+), 256 deletions(-) >> delete mode 100644 drivers/firmware/efi/earlycon.c >> create mode 100644 drivers/video/console/earlycon.c >> >> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt >> index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644 >> --- a/Documentation/admin-guide/kernel-parameters.txt >> +++ b/Documentation/admin-guide/kernel-parameters.txt >> @@ -1281,12 +1281,9 @@ >> specified address. The serial port must already be >> setup and configured. Options are not yet supported. >> >> - efifb,[options] >> + efifb >> Start an early, unaccelerated console on the EFI >> - memory mapped framebuffer (if available). On cache >> - coherent non-x86 systems that use system memory for >> - the framebuffer, pass the 'ram' option so that it is >> - mapped with the correct attributes. >> + memory mapped framebuffer (if available). >> >> linflex,<addr> >> Use early console provided by Freescale LINFlexD UART >> @@ -1294,6 +1291,11 @@ >> address must be provided, and the serial port must >> already be setup and configured. >> >> + simplefb,<addr>,<width>x<height>x<bpp> >> + Use early console with simple framebuffer that is >> + pre-initialized by firmware. A valid base address, >> + width, height and pixel size must be provided. >> + >> earlyprintk= [X86,SH,ARM,M68k,S390] >> earlyprintk=vga >> earlyprintk=sclp >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ >> T: git git://linuxtv.org/anttip/media_tree.git >> F: drivers/media/tuners/e4000* >> >> +EARLY CONSOLE FRAMEBUFFER DRIVER >> +M: Markuss Broks <markuss.broks@gmail.com> >> +S: Maintained >> +F: drivers/video/console/earlycon.c >> + >> EARTH_PT1 MEDIA DRIVER >> M: Akihiro Tsukada <tskd08@gmail.com> >> L: linux-media@vger.kernel.org >> diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig >> index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644 >> --- a/drivers/firmware/efi/Kconfig >> +++ b/drivers/firmware/efi/Kconfig >> @@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA >> may be used to override this option. >> >> config EFI_EARLYCON >> - def_bool y >> - depends on SERIAL_EARLYCON && !ARM && !IA64 >> - select FONT_SUPPORT >> - select ARCH_USE_MEMREMAP_PROT >> + bool "EFI early console support" >> + select FB_EARLYCON >> + default y >> >> config EFI_CUSTOM_SSDT_OVERLAYS >> bool "Load custom ACPI SSDT overlay from an EFI variable" >> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile >> index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644 >> --- a/drivers/firmware/efi/Makefile >> +++ b/drivers/firmware/efi/Makefile >> @@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y) >> riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o >> obj-$(CONFIG_RISCV) += $(riscv-obj-y) >> obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o >> -obj-$(CONFIG_EFI_EARLYCON) += earlycon.o >> obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o >> obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o >> diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c >> deleted file mode 100644 >> index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000 >> --- a/drivers/firmware/efi/earlycon.c >> +++ /dev/null >> @@ -1,246 +0,0 @@ >> -// SPDX-License-Identifier: GPL-2.0 >> -/* >> - * Copyright (C) 2013 Intel Corporation; author Matt Fleming >> - */ >> - >> -#include <linux/console.h> >> -#include <linux/efi.h> >> -#include <linux/font.h> >> -#include <linux/io.h> >> -#include <linux/kernel.h> >> -#include <linux/serial_core.h> >> -#include <linux/screen_info.h> >> - >> -#include <asm/early_ioremap.h> >> - >> -static const struct console *earlycon_console __initdata; >> -static const struct font_desc *font; >> -static u32 efi_x, efi_y; >> -static u64 fb_base; >> -static bool fb_wb; >> -static void *efi_fb; >> - >> -/* >> - * EFI earlycon needs to use early_memremap() to map the framebuffer. >> - * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon', >> - * memremap() should be used instead. memremap() will be available after >> - * paging_init() which is earlier than initcall callbacks. Thus adding this >> - * early initcall function early_efi_map_fb() to map the whole EFI framebuffer. >> - */ >> -static int __init efi_earlycon_remap_fb(void) >> -{ >> - /* bail if there is no bootconsole or it has been disabled already */ >> - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) >> - return 0; >> - >> - efi_fb = memremap(fb_base, screen_info.lfb_size, >> - fb_wb ? MEMREMAP_WB : MEMREMAP_WC); >> - >> - return efi_fb ? 0 : -ENOMEM; >> -} >> -early_initcall(efi_earlycon_remap_fb); >> - >> -static int __init efi_earlycon_unmap_fb(void) >> -{ >> - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ >> - if (efi_fb && !(earlycon_console->flags & CON_ENABLED)) >> - memunmap(efi_fb); >> - return 0; >> -} >> -late_initcall(efi_earlycon_unmap_fb); >> - >> -static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) >> -{ >> - pgprot_t fb_prot; >> - >> - if (efi_fb) >> - return efi_fb + start; >> - >> - fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL); >> - return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); >> -} >> - >> -static __ref void efi_earlycon_unmap(void *addr, unsigned long len) >> -{ >> - if (efi_fb) >> - return; >> - >> - early_memunmap(addr, len); >> -} >> - >> -static void efi_earlycon_clear_scanline(unsigned int y) >> -{ >> - unsigned long *dst; >> - u16 len; >> - >> - len = screen_info.lfb_linelength; >> - dst = efi_earlycon_map(y*len, len); >> - if (!dst) >> - return; >> - >> - memset(dst, 0, len); >> - efi_earlycon_unmap(dst, len); >> -} >> - >> -static void efi_earlycon_scroll_up(void) >> -{ >> - unsigned long *dst, *src; >> - u16 len; >> - u32 i, height; >> - >> - len = screen_info.lfb_linelength; >> - height = screen_info.lfb_height; >> - >> - for (i = 0; i < height - font->height; i++) { >> - dst = efi_earlycon_map(i*len, len); >> - if (!dst) >> - return; >> - >> - src = efi_earlycon_map((i + font->height) * len, len); >> - if (!src) { >> - efi_earlycon_unmap(dst, len); >> - return; >> - } >> - >> - memmove(dst, src, len); >> - >> - efi_earlycon_unmap(src, len); >> - efi_earlycon_unmap(dst, len); >> - } >> -} >> - >> -static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) >> -{ >> - const u32 color_black = 0x00000000; >> - const u32 color_white = 0x00ffffff; >> - const u8 *src; >> - int m, n, bytes; >> - u8 x; >> - >> - bytes = BITS_TO_BYTES(font->width); >> - src = font->data + c * font->height * bytes + h * bytes; >> - >> - for (m = 0; m < font->width; m++) { >> - n = m % 8; >> - x = *(src + m / 8); >> - if ((x >> (7 - n)) & 1) >> - *dst = color_white; >> - else >> - *dst = color_black; >> - dst++; >> - } >> -} >> - >> -static void >> -efi_earlycon_write(struct console *con, const char *str, unsigned int num) >> -{ >> - struct screen_info *si; >> - unsigned int len; >> - const char *s; >> - void *dst; >> - >> - si = &screen_info; >> - len = si->lfb_linelength; >> - >> - while (num) { >> - unsigned int linemax; >> - unsigned int h, count = 0; >> - >> - for (s = str; *s && *s != '\n'; s++) { >> - if (count == num) >> - break; >> - count++; >> - } >> - >> - linemax = (si->lfb_width - efi_x) / font->width; >> - if (count > linemax) >> - count = linemax; >> - >> - for (h = 0; h < font->height; h++) { >> - unsigned int n, x; >> - >> - dst = efi_earlycon_map((efi_y + h) * len, len); >> - if (!dst) >> - return; >> - >> - s = str; >> - n = count; >> - x = efi_x; >> - >> - while (n-- > 0) { >> - efi_earlycon_write_char(dst + x*4, *s, h); >> - x += font->width; >> - s++; >> - } >> - >> - efi_earlycon_unmap(dst, len); >> - } >> - >> - num -= count; >> - efi_x += count * font->width; >> - str += count; >> - >> - if (num > 0 && *s == '\n') { >> - efi_x = 0; >> - efi_y += font->height; >> - str++; >> - num--; >> - } >> - >> - if (efi_x + font->width > si->lfb_width) { >> - efi_x = 0; >> - efi_y += font->height; >> - } >> - >> - if (efi_y + font->height > si->lfb_height) { >> - u32 i; >> - >> - efi_y -= font->height; >> - efi_earlycon_scroll_up(); >> - >> - for (i = 0; i < font->height; i++) >> - efi_earlycon_clear_scanline(efi_y + i); >> - } >> - } >> -} >> - >> -static int __init efi_earlycon_setup(struct earlycon_device *device, >> - const char *opt) >> -{ >> - struct screen_info *si; >> - u16 xres, yres; >> - u32 i; >> - >> - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) >> - return -ENODEV; >> - >> - fb_base = screen_info.lfb_base; >> - if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) >> - fb_base |= (u64)screen_info.ext_lfb_base << 32; >> - >> - fb_wb = opt && !strcmp(opt, "ram"); >> - >> - si = &screen_info; >> - xres = si->lfb_width; >> - yres = si->lfb_height; >> - >> - /* >> - * efi_earlycon_write_char() implicitly assumes a framebuffer with >> - * 32 bits per pixel. >> - */ >> - if (si->lfb_depth != 32) >> - return -ENODEV; >> - >> - font = get_default_font(xres, yres, -1, -1); >> - if (!font) >> - return -ENODEV; >> - >> - efi_y = rounddown(yres, font->height) - font->height; >> - for (i = 0; i < (yres - efi_y) / font->height; i++) >> - efi_earlycon_scroll_up(); >> - >> - device->con->write = efi_earlycon_write; >> - earlycon_console = device->con; >> - return 0; >> -} >> -EARLYCON_DECLARE(efifb, efi_earlycon_setup); >> diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig >> index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644 >> --- a/drivers/video/console/Kconfig >> +++ b/drivers/video/console/Kconfig >> @@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS >> monitor. >> Select 25 if you use a 640x480 resolution by default. >> >> +config FB_EARLYCON >> + bool "Generic framebuffer early console" >> + depends on SERIAL_EARLYCON && !ARM && !IA64 >> + select FONT_SUPPORT >> + select ARCH_USE_MEMREMAP_PROT >> + help >> + Say Y here if you want early console support for firmware established >> + linear framebuffer. Unless you are using EFI framebuffer, you need to >> + specify framebuffer geometry and address in device-tree or in kernel >> + command line. >> + >> config FRAMEBUFFER_CONSOLE >> bool "Framebuffer Console support" >> depends on FB && !UML >> diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile >> index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644 >> --- a/drivers/video/console/Makefile >> +++ b/drivers/video/console/Makefile >> @@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o >> obj-$(CONFIG_VGA_CONSOLE) += vgacon.o >> obj-$(CONFIG_MDA_CONSOLE) += mdacon.o >> >> +obj-$(CONFIG_FB_EARLYCON) += earlycon.o >> obj-$(CONFIG_FB_STI) += sticore.o >> diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c >> new file mode 100644 >> index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5 >> --- /dev/null >> +++ b/drivers/video/console/earlycon.c >> @@ -0,0 +1,305 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Copyright (C) 2013 Intel Corporation; author Matt Fleming >> + * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com> >> + */ >> + >> +#include <asm/early_ioremap.h> >> +#include <linux/console.h> >> +#include <linux/efi.h> >> +#include <linux/font.h> >> +#include <linux/io.h> >> +#include <linux/kernel.h> >> +#include <linux/mm.h> >> +#include <linux/of.h> >> +#include <linux/of_fdt.h> >> +#include <linux/serial_core.h> >> +#include <linux/screen_info.h> >> + >> +struct fb_earlycon { >> + u32 x, y, curr_x, curr_y, depth, stride; >> + size_t size; >> + phys_addr_t phys_base; >> + void __iomem *virt_base; >> +}; >> + >> +static const struct console *earlycon_console __initconst; >> +static struct fb_earlycon info; >> +static const struct font_desc *font; >> + >> +static int __init simplefb_earlycon_remap_fb(void) >> +{ >> + unsigned long mapping; >> + /* bail if there is no bootconsole or it has been disabled already */ >> + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) >> + return 0; >> + >> + if (region_intersects(info.phys_base, info.size, >> + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS) >> + mapping = MEMREMAP_WB; >> + else >> + mapping = MEMREMAP_WC; >> + >> + info.virt_base = memremap(info.phys_base, info.size, mapping); >> + >> + return info.virt_base ? 0 : -ENOMEM; >> +} >> +early_initcall(simplefb_earlycon_remap_fb); >> + >> +static int __init simplefb_earlycon_unmap_fb(void) >> +{ >> + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ >> + if (info.virt_base && !(earlycon_console->flags & CON_ENABLED)) >> + memunmap(info.virt_base); >> + return 0; >> +} >> +late_initcall(simplefb_earlycon_unmap_fb); >> + >> +static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len) >> +{ >> + pgprot_t fb_prot; >> + >> + if (info.virt_base) >> + return info.virt_base + start; >> + >> + fb_prot = PAGE_KERNEL; >> + return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot)); >> +} >> + >> +static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len) >> +{ >> + if (info.virt_base) >> + return; >> + >> + early_memunmap(addr, len); >> +} >> + >> +static void simplefb_earlycon_clear_scanline(unsigned int y) >> +{ >> + unsigned long *dst; >> + u16 len; >> + >> + len = info.stride; >> + dst = simplefb_earlycon_map(y * len, len); >> + if (!dst) >> + return; >> + >> + memset(dst, 0, len); >> + simplefb_earlycon_unmap(dst, len); >> +} >> + >> +static void simplefb_earlycon_scroll_up(void) >> +{ >> + unsigned long *dst, *src; >> + u16 len; >> + u32 i, height; >> + >> + len = info.stride; >> + height = info.y; >> + >> + for (i = 0; i < height - font->height; i++) { >> + dst = simplefb_earlycon_map(i * len, len); >> + if (!dst) >> + return; >> + >> + src = simplefb_earlycon_map((i + font->height) * len, len); >> + if (!src) { >> + simplefb_earlycon_unmap(dst, len); >> + return; >> + } >> + >> + memmove(dst, src, len); >> + >> + simplefb_earlycon_unmap(src, len); >> + simplefb_earlycon_unmap(dst, len); >> + } >> +} >> + >> +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h) >> +{ >> + const u8 *src; >> + int m, n, bytes; >> + u8 x; >> + >> + bytes = BITS_TO_BYTES(font->width); >> + src = font->data + c * font->height * bytes + h * bytes; >> + >> + for (m = 0; m < font->width; m++) { >> + n = m % 8; >> + x = *(src + m / 8); >> + if ((x >> (7 - n)) & 1) >> + memset(dst, 0xff, (info.depth / 8)); >> + else >> + memset(dst, 0, (info.depth / 8)); >> + dst += (info.depth / 8); >> + } >> +} >> + >> +static void >> +simplefb_earlycon_write(struct console *con, const char *str, unsigned int num) >> +{ >> + unsigned int len; >> + const char *s; >> + void *dst; >> + >> + len = info.stride; >> + >> + while (num) { >> + unsigned int linemax; >> + unsigned int h, count = 0; >> + >> + for (s = str; *s && *s != '\n'; s++) { >> + if (count == num) >> + break; >> + count++; >> + } >> + >> + linemax = (info.x - info.curr_x) / font->width; >> + if (count > linemax) >> + count = linemax; >> + >> + for (h = 0; h < font->height; h++) { >> + unsigned int n, x; >> + >> + dst = simplefb_earlycon_map((info.curr_y + h) * len, len); >> + if (!dst) >> + return; >> + >> + s = str; >> + n = count; >> + x = info.curr_x; >> + >> + while (n-- > 0) { >> + simplefb_earlycon_write_char(dst + (x * 4), *s, h); >> + x += font->width; >> + s++; >> + } >> + >> + simplefb_earlycon_unmap(dst, len); >> + } >> + >> + num -= count; >> + info.curr_x += count * font->width; >> + str += count; >> + >> + if (num > 0 && *s == '\n') { >> + info.curr_x = 0; >> + info.curr_y += font->height; >> + str++; >> + num--; >> + } >> + >> + if (info.curr_x + font->width > info.x) { >> + info.curr_x = 0; >> + info.curr_y += font->height; >> + } >> + >> + if (info.curr_y + font->height > info.y) { >> + u32 i; >> + >> + info.curr_y -= font->height; >> + simplefb_earlycon_scroll_up(); >> + >> + for (i = 0; i < font->height; i++) >> + simplefb_earlycon_clear_scanline(info.curr_y + i); >> + } >> + } >> +} >> + >> +static int __init simplefb_earlycon_setup_common(struct earlycon_device *device, >> + const char *opt) >> +{ >> + int i; >> + >> + info.size = info.x * info.y * (info.depth / 8); >> + >> + font = get_default_font(info.x, info.y, -1, -1); >> + if (!font) >> + return -ENODEV; >> + >> + info.curr_y = rounddown(info.y, font->height) - font->height; >> + for (i = 0; i < (info.y - info.curr_y) / font->height; i++) >> + simplefb_earlycon_scroll_up(); >> + >> + device->con->write = simplefb_earlycon_write; >> + earlycon_console = device->con; >> + return 0; >> +} >> + >> +static int __init simplefb_earlycon_setup(struct earlycon_device *device, >> + const char *opt) >> +{ >> + struct uart_port *port = &device->port; >> + int ret; >> + >> + if (!port->mapbase) >> + return -ENODEV; >> + >> + info.phys_base = port->mapbase; >> + >> + ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth); >> + if (ret != 3) >> + return -ENODEV; >> + >> + info.stride = info.x * (info.depth / 8); >> + >> + return simplefb_earlycon_setup_common(device, opt); >> +} >> + >> +EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup); >> + >> +#ifdef CONFIG_EFI_EARLYCON >> +static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device, >> + const char *opt) >> +{ >> + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) >> + return -ENODEV; >> + >> + info.phys_base = screen_info.lfb_base; >> + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) >> + info.phys_base |= (u64)screen_info.ext_lfb_base << 32; >> + >> + info.x = screen_info.lfb_width; >> + info.y = screen_info.lfb_height; >> + info.depth = screen_info.lfb_depth; >> + info.stride = screen_info.lfb_linelength; >> + >> + return simplefb_earlycon_setup_common(device, opt); >> +} >> + >> +EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi); >> +#endif >> + >> +#ifdef CONFIG_OF_EARLY_FLATTREE >> +static int __init simplefb_earlycon_setup_of(struct earlycon_device *device, >> + const char *opt) >> +{ >> + struct uart_port *port = &device->port; >> + const __be32 *val; >> + >> + if (!port->mapbase) >> + return -ENODEV; >> + >> + info.phys_base = port->mapbase; >> + >> + val = of_get_flat_dt_prop(device->offset, "width", NULL); >> + if (!val) >> + return -ENODEV; >> + info.x = be32_to_cpu(*val); >> + >> + val = of_get_flat_dt_prop(device->offset, "height", NULL); >> + if (!val) >> + return -ENODEV; >> + info.y = be32_to_cpu(*val); >> + >> + val = of_get_flat_dt_prop(device->offset, "stride", NULL); >> + if (!val) >> + return -ENODEV; >> + info.stride = be32_to_cpu(*val); >> + info.depth = (info.stride / info.x) * 8; >> + >> + return simplefb_earlycon_setup_common(device, opt); >> +} >> + >> +OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of); >> +#endif >> -- >> 2.37.0 >> >
On Tue, Sep 6, 2022, at 11:52 PM, Daniel Vetter wrote: > On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote: > > Ok I have a more fundamental issue with this than the lack of proper patch > splitting I mentioned in the other thread. > > This is the wrong place. > > drivers/video/console is about the various vt console implementations, > which supply a struct consw to con_register_driver. You are right, that was my mistake. The original patch was for drivers/video/fbdev/, and I suggested moving it out of there because it does not depend on the fbdev subsystem, but clearly my suggestions of drivers/video/console was equally wrong. > This otoh is an (early) kernel/printk console implemented using struct > console. Totally different thing, and really shouldn't end up in > drivers/video/console imo. Somewhere in drivers/firmware might still be > the best place, the sysfb stuff is also there. Maybe > drivers/firmware/sysfb_earlycon.c? drivers/firmware/ is better but doesn't sound great to me either, since one important thing the patch does is to not make it depend on EFI firmware or BIOS style screen_info any more. Maybe drivers/tty/earlycon_simplefb.c would work better, keeping it close to the earlycon base support in drivers/tty/serial/, the vt console and the old simplefb driver/, without tying to too closely to fbdev of UEFI. Arnd
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1281,12 +1281,9 @@ specified address. The serial port must already be setup and configured. Options are not yet supported. - efifb,[options] + efifb Start an early, unaccelerated console on the EFI - memory mapped framebuffer (if available). On cache - coherent non-x86 systems that use system memory for - the framebuffer, pass the 'ram' option so that it is - mapped with the correct attributes. + memory mapped framebuffer (if available). linflex,<addr> Use early console provided by Freescale LINFlexD UART @@ -1294,6 +1291,11 @@ address must be provided, and the serial port must already be setup and configured. + simplefb,<addr>,<width>x<height>x<bpp> + Use early console with simple framebuffer that is + pre-initialized by firmware. A valid base address, + width, height and pixel size must be provided. + earlyprintk= [X86,SH,ARM,M68k,S390] earlyprintk=vga earlyprintk=sclp diff --git a/MAINTAINERS b/MAINTAINERS index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/anttip/media_tree.git F: drivers/media/tuners/e4000* +EARLY CONSOLE FRAMEBUFFER DRIVER +M: Markuss Broks <markuss.broks@gmail.com> +S: Maintained +F: drivers/video/console/earlycon.c + EARTH_PT1 MEDIA DRIVER M: Akihiro Tsukada <tskd08@gmail.com> L: linux-media@vger.kernel.org diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA may be used to override this option. config EFI_EARLYCON - def_bool y - depends on SERIAL_EARLYCON && !ARM && !IA64 - select FONT_SUPPORT - select ARCH_USE_MEMREMAP_PROT + bool "EFI early console support" + select FB_EARLYCON + default y config EFI_CUSTOM_SSDT_OVERLAYS bool "Load custom ACPI SSDT overlay from an EFI variable" diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y) riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o obj-$(CONFIG_RISCV) += $(riscv-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o -obj-$(CONFIG_EFI_EARLYCON) += earlycon.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c deleted file mode 100644 index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000 --- a/drivers/firmware/efi/earlycon.c +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2013 Intel Corporation; author Matt Fleming - */ - -#include <linux/console.h> -#include <linux/efi.h> -#include <linux/font.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/serial_core.h> -#include <linux/screen_info.h> - -#include <asm/early_ioremap.h> - -static const struct console *earlycon_console __initdata; -static const struct font_desc *font; -static u32 efi_x, efi_y; -static u64 fb_base; -static bool fb_wb; -static void *efi_fb; - -/* - * EFI earlycon needs to use early_memremap() to map the framebuffer. - * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon', - * memremap() should be used instead. memremap() will be available after - * paging_init() which is earlier than initcall callbacks. Thus adding this - * early initcall function early_efi_map_fb() to map the whole EFI framebuffer. - */ -static int __init efi_earlycon_remap_fb(void) -{ - /* bail if there is no bootconsole or it has been disabled already */ - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) - return 0; - - efi_fb = memremap(fb_base, screen_info.lfb_size, - fb_wb ? MEMREMAP_WB : MEMREMAP_WC); - - return efi_fb ? 0 : -ENOMEM; -} -early_initcall(efi_earlycon_remap_fb); - -static int __init efi_earlycon_unmap_fb(void) -{ - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ - if (efi_fb && !(earlycon_console->flags & CON_ENABLED)) - memunmap(efi_fb); - return 0; -} -late_initcall(efi_earlycon_unmap_fb); - -static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) -{ - pgprot_t fb_prot; - - if (efi_fb) - return efi_fb + start; - - fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL); - return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); -} - -static __ref void efi_earlycon_unmap(void *addr, unsigned long len) -{ - if (efi_fb) - return; - - early_memunmap(addr, len); -} - -static void efi_earlycon_clear_scanline(unsigned int y) -{ - unsigned long *dst; - u16 len; - - len = screen_info.lfb_linelength; - dst = efi_earlycon_map(y*len, len); - if (!dst) - return; - - memset(dst, 0, len); - efi_earlycon_unmap(dst, len); -} - -static void efi_earlycon_scroll_up(void) -{ - unsigned long *dst, *src; - u16 len; - u32 i, height; - - len = screen_info.lfb_linelength; - height = screen_info.lfb_height; - - for (i = 0; i < height - font->height; i++) { - dst = efi_earlycon_map(i*len, len); - if (!dst) - return; - - src = efi_earlycon_map((i + font->height) * len, len); - if (!src) { - efi_earlycon_unmap(dst, len); - return; - } - - memmove(dst, src, len); - - efi_earlycon_unmap(src, len); - efi_earlycon_unmap(dst, len); - } -} - -static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) -{ - const u32 color_black = 0x00000000; - const u32 color_white = 0x00ffffff; - const u8 *src; - int m, n, bytes; - u8 x; - - bytes = BITS_TO_BYTES(font->width); - src = font->data + c * font->height * bytes + h * bytes; - - for (m = 0; m < font->width; m++) { - n = m % 8; - x = *(src + m / 8); - if ((x >> (7 - n)) & 1) - *dst = color_white; - else - *dst = color_black; - dst++; - } -} - -static void -efi_earlycon_write(struct console *con, const char *str, unsigned int num) -{ - struct screen_info *si; - unsigned int len; - const char *s; - void *dst; - - si = &screen_info; - len = si->lfb_linelength; - - while (num) { - unsigned int linemax; - unsigned int h, count = 0; - - for (s = str; *s && *s != '\n'; s++) { - if (count == num) - break; - count++; - } - - linemax = (si->lfb_width - efi_x) / font->width; - if (count > linemax) - count = linemax; - - for (h = 0; h < font->height; h++) { - unsigned int n, x; - - dst = efi_earlycon_map((efi_y + h) * len, len); - if (!dst) - return; - - s = str; - n = count; - x = efi_x; - - while (n-- > 0) { - efi_earlycon_write_char(dst + x*4, *s, h); - x += font->width; - s++; - } - - efi_earlycon_unmap(dst, len); - } - - num -= count; - efi_x += count * font->width; - str += count; - - if (num > 0 && *s == '\n') { - efi_x = 0; - efi_y += font->height; - str++; - num--; - } - - if (efi_x + font->width > si->lfb_width) { - efi_x = 0; - efi_y += font->height; - } - - if (efi_y + font->height > si->lfb_height) { - u32 i; - - efi_y -= font->height; - efi_earlycon_scroll_up(); - - for (i = 0; i < font->height; i++) - efi_earlycon_clear_scanline(efi_y + i); - } - } -} - -static int __init efi_earlycon_setup(struct earlycon_device *device, - const char *opt) -{ - struct screen_info *si; - u16 xres, yres; - u32 i; - - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) - return -ENODEV; - - fb_base = screen_info.lfb_base; - if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) - fb_base |= (u64)screen_info.ext_lfb_base << 32; - - fb_wb = opt && !strcmp(opt, "ram"); - - si = &screen_info; - xres = si->lfb_width; - yres = si->lfb_height; - - /* - * efi_earlycon_write_char() implicitly assumes a framebuffer with - * 32 bits per pixel. - */ - if (si->lfb_depth != 32) - return -ENODEV; - - font = get_default_font(xres, yres, -1, -1); - if (!font) - return -ENODEV; - - efi_y = rounddown(yres, font->height) - font->height; - for (i = 0; i < (yres - efi_y) / font->height; i++) - efi_earlycon_scroll_up(); - - device->con->write = efi_earlycon_write; - earlycon_console = device->con; - return 0; -} -EARLYCON_DECLARE(efifb, efi_earlycon_setup); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS monitor. Select 25 if you use a 640x480 resolution by default. +config FB_EARLYCON + bool "Generic framebuffer early console" + depends on SERIAL_EARLYCON && !ARM && !IA64 + select FONT_SUPPORT + select ARCH_USE_MEMREMAP_PROT + help + Say Y here if you want early console support for firmware established + linear framebuffer. Unless you are using EFI framebuffer, you need to + specify framebuffer geometry and address in device-tree or in kernel + command line. + config FRAMEBUFFER_CONSOLE bool "Framebuffer Console support" depends on FB && !UML diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o obj-$(CONFIG_MDA_CONSOLE) += mdacon.o +obj-$(CONFIG_FB_EARLYCON) += earlycon.o obj-$(CONFIG_FB_STI) += sticore.o diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c new file mode 100644 index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5 --- /dev/null +++ b/drivers/video/console/earlycon.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013 Intel Corporation; author Matt Fleming + * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com> + */ + +#include <asm/early_ioremap.h> +#include <linux/console.h> +#include <linux/efi.h> +#include <linux/font.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/serial_core.h> +#include <linux/screen_info.h> + +struct fb_earlycon { + u32 x, y, curr_x, curr_y, depth, stride; + size_t size; + phys_addr_t phys_base; + void __iomem *virt_base; +}; + +static const struct console *earlycon_console __initconst; +static struct fb_earlycon info; +static const struct font_desc *font; + +static int __init simplefb_earlycon_remap_fb(void) +{ + unsigned long mapping; + /* bail if there is no bootconsole or it has been disabled already */ + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) + return 0; + + if (region_intersects(info.phys_base, info.size, + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS) + mapping = MEMREMAP_WB; + else + mapping = MEMREMAP_WC; + + info.virt_base = memremap(info.phys_base, info.size, mapping); + + return info.virt_base ? 0 : -ENOMEM; +} +early_initcall(simplefb_earlycon_remap_fb); + +static int __init simplefb_earlycon_unmap_fb(void) +{ + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ + if (info.virt_base && !(earlycon_console->flags & CON_ENABLED)) + memunmap(info.virt_base); + return 0; +} +late_initcall(simplefb_earlycon_unmap_fb); + +static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len) +{ + pgprot_t fb_prot; + + if (info.virt_base) + return info.virt_base + start; + + fb_prot = PAGE_KERNEL; + return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot)); +} + +static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len) +{ + if (info.virt_base) + return; + + early_memunmap(addr, len); +} + +static void simplefb_earlycon_clear_scanline(unsigned int y) +{ + unsigned long *dst; + u16 len; + + len = info.stride; + dst = simplefb_earlycon_map(y * len, len); + if (!dst) + return; + + memset(dst, 0, len); + simplefb_earlycon_unmap(dst, len); +} + +static void simplefb_earlycon_scroll_up(void) +{ + unsigned long *dst, *src; + u16 len; + u32 i, height; + + len = info.stride; + height = info.y; + + for (i = 0; i < height - font->height; i++) { + dst = simplefb_earlycon_map(i * len, len); + if (!dst) + return; + + src = simplefb_earlycon_map((i + font->height) * len, len); + if (!src) { + simplefb_earlycon_unmap(dst, len); + return; + } + + memmove(dst, src, len); + + simplefb_earlycon_unmap(src, len); + simplefb_earlycon_unmap(dst, len); + } +} + +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h) +{ + const u8 *src; + int m, n, bytes; + u8 x; + + bytes = BITS_TO_BYTES(font->width); + src = font->data + c * font->height * bytes + h * bytes; + + for (m = 0; m < font->width; m++) { + n = m % 8; + x = *(src + m / 8); + if ((x >> (7 - n)) & 1) + memset(dst, 0xff, (info.depth / 8)); + else + memset(dst, 0, (info.depth / 8)); + dst += (info.depth / 8); + } +} + +static void +simplefb_earlycon_write(struct console *con, const char *str, unsigned int num) +{ + unsigned int len; + const char *s; + void *dst; + + len = info.stride; + + while (num) { + unsigned int linemax; + unsigned int h, count = 0; + + for (s = str; *s && *s != '\n'; s++) { + if (count == num) + break; + count++; + } + + linemax = (info.x - info.curr_x) / font->width; + if (count > linemax) + count = linemax; + + for (h = 0; h < font->height; h++) { + unsigned int n, x; + + dst = simplefb_earlycon_map((info.curr_y + h) * len, len); + if (!dst) + return; + + s = str; + n = count; + x = info.curr_x; + + while (n-- > 0) { + simplefb_earlycon_write_char(dst + (x * 4), *s, h); + x += font->width; + s++; + } + + simplefb_earlycon_unmap(dst, len); + } + + num -= count; + info.curr_x += count * font->width; + str += count; + + if (num > 0 && *s == '\n') { + info.curr_x = 0; + info.curr_y += font->height; + str++; + num--; + } + + if (info.curr_x + font->width > info.x) { + info.curr_x = 0; + info.curr_y += font->height; + } + + if (info.curr_y + font->height > info.y) { + u32 i; + + info.curr_y -= font->height; + simplefb_earlycon_scroll_up(); + + for (i = 0; i < font->height; i++) + simplefb_earlycon_clear_scanline(info.curr_y + i); + } + } +} + +static int __init simplefb_earlycon_setup_common(struct earlycon_device *device, + const char *opt) +{ + int i; + + info.size = info.x * info.y * (info.depth / 8); + + font = get_default_font(info.x, info.y, -1, -1); + if (!font) + return -ENODEV; + + info.curr_y = rounddown(info.y, font->height) - font->height; + for (i = 0; i < (info.y - info.curr_y) / font->height; i++) + simplefb_earlycon_scroll_up(); + + device->con->write = simplefb_earlycon_write; + earlycon_console = device->con; + return 0; +} + +static int __init simplefb_earlycon_setup(struct earlycon_device *device, + const char *opt) +{ + struct uart_port *port = &device->port; + int ret; + + if (!port->mapbase) + return -ENODEV; + + info.phys_base = port->mapbase; + + ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth); + if (ret != 3) + return -ENODEV; + + info.stride = info.x * (info.depth / 8); + + return simplefb_earlycon_setup_common(device, opt); +} + +EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup); + +#ifdef CONFIG_EFI_EARLYCON +static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device, + const char *opt) +{ + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) + return -ENODEV; + + info.phys_base = screen_info.lfb_base; + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) + info.phys_base |= (u64)screen_info.ext_lfb_base << 32; + + info.x = screen_info.lfb_width; + info.y = screen_info.lfb_height; + info.depth = screen_info.lfb_depth; + info.stride = screen_info.lfb_linelength; + + return simplefb_earlycon_setup_common(device, opt); +} + +EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi); +#endif + +#ifdef CONFIG_OF_EARLY_FLATTREE +static int __init simplefb_earlycon_setup_of(struct earlycon_device *device, + const char *opt) +{ + struct uart_port *port = &device->port; + const __be32 *val; + + if (!port->mapbase) + return -ENODEV; + + info.phys_base = port->mapbase; + + val = of_get_flat_dt_prop(device->offset, "width", NULL); + if (!val) + return -ENODEV; + info.x = be32_to_cpu(*val); + + val = of_get_flat_dt_prop(device->offset, "height", NULL); + if (!val) + return -ENODEV; + info.y = be32_to_cpu(*val); + + val = of_get_flat_dt_prop(device->offset, "stride", NULL); + if (!val) + return -ENODEV; + info.stride = be32_to_cpu(*val); + info.depth = (info.stride / info.x) * 8; + + return simplefb_earlycon_setup_common(device, opt); +} + +OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of); +#endif
Add early console support for generic linear framebuffer devices. This driver supports probing from cmdline early parameters or from the device-tree using information in simple-framebuffer node. The EFI functionality should be retained in whole. The driver was disabled on ARM because of a bug in early_ioremap implementation on ARM and on IA64 because of lack of early_memremap_prot. Signed-off-by: Markuss Broks <markuss.broks@gmail.com> --- .../admin-guide/kernel-parameters.txt | 12 +- MAINTAINERS | 5 + drivers/firmware/efi/Kconfig | 7 +- drivers/firmware/efi/Makefile | 1 - drivers/firmware/efi/earlycon.c | 246 -------------- drivers/video/console/Kconfig | 11 + drivers/video/console/Makefile | 1 + drivers/video/console/earlycon.c | 305 ++++++++++++++++++ 8 files changed, 332 insertions(+), 256 deletions(-) delete mode 100644 drivers/firmware/efi/earlycon.c create mode 100644 drivers/video/console/earlycon.c