efifb是uefi中提供的显示framebuffer。机器在bios阶段的显示是固件中提供了显卡的驱动,这种显示能力通用可以直接给linux内核使用。
[ 6.066376] pci 0000:0f:00.0: BAR 0: assigned to efifb
[ 15.946082] efifb: probing for efifb
[ 15.949650] pci 0000:0f:00.0: BAR has moved, updating efifb address
[ 15.955913] efifb: framebuffer at 0x1060000000, using 1216k, total 1200k
[ 15.962603] efifb: mode is 640x480x32, linelength=2560, pages=1
[ 15.968509] efifb: scrolling: redraw
[ 15.972071] efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
[ 16.274339] fb0: EFI VGA frame buffer device
生成的设备节点/dev/fb0
在arm64上有arch/arm64/kernel/efi.c定义了
struct screen_info screen_info __section(.data); //screen_info用来获取uefi提供的显示信息。
代码在 drivers/firmware/efi/efi-init.c 中
static void __init init_screen_info(void)
{
struct screen_info *si;
if (IS_ENABLED(CONFIG_ARM) &&
screen_info_table != EFI_INVALID_TABLE_ADDR) {
si = early_memremap_ro(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
return;
}
screen_info = *si; //获取
early_memunmap(si, sizeof(*si));
/* dummycon on ARM needs non-zero values for columns/lines */
screen_info.orig_video_cols = 80;
screen_info.orig_video_lines = 25;
}
。。。。
}
创建efi-framebuffer的platform设备
static int __init register_gop_device(void)
{
struct platform_device *pd;
int err;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return 0;
pd = platform_device_alloc("efi-framebuffer", 0);
if (!pd)
return -ENOMEM;
if (IS_ENABLED(CONFIG_PCI))
pd->dev.fwnode = &efifb_fwnode;
err = platform_device_add_data(pd, &screen_info, sizeof(screen_info));
if (err)
return err;
return platform_device_add(pd);
}
subsys_initcall(register_gop_device);
screen_info的内容可以通过gdb查看: 如果获取的内容都为0 ,则bios没有能够提供efifb功能。
gdb linux-5.10/vmlinux /proc/kcore
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from linux-5.10/vmlinux...
p [New process 1]
Core was generated by `BOOT_IMAGE=/boot/vmlinuz-5.10.0+ root=UUID=610f0a54-4031-4da0-a4c9-a0d0044f8319'.
#0 0x0000000000000000 in ?? ()
(gdb) p screen_info
$1 = {orig_x = 0 '\000', orig_y = 0 '\000', ext_mem_k = 0,
orig_video_page = 0, orig_video_mode = 0 '\000', orig_video_cols = 0 '\000',
flags = 0 '\000', unused2 = 0 '\000', orig_video_ega_bx = 0, unused3 = 0,
orig_video_lines = 0 '\000', orig_video_isVGA = 112 'p',
orig_video_points = 0, lfb_width = 640, lfb_height = 480, lfb_depth = 32,
lfb_base = 1073741824, lfb_size = 1228800, cl_magic = 0, cl_offset = 0,
lfb_linelength = 2560, red_size = 8 '\b', red_pos = 16 '\020',
green_size = 8 '\b', green_pos = 8 '\b', blue_size = 8 '\b',
blue_pos = 0 '\000', rsvd_size = 8 '\b', rsvd_pos = 24 '\030',
vesapm_seg = 0, vesapm_off = 0, pages = 1, vesa_attributes = 0,
capabilities = 3, ext_lfb_base = 16, _reserved = "\000"}
(gdb)
相应的efi-framebuffer驱动在drivers/video/fbdev/efifb.c:
static struct platform_driver efifb_driver = {
.driver = {
.name = "efi-framebuffer",
},
.probe = efifb_probe,
.remove = efifb_remove,
};
在函数efifb_probe中会将screen_info中的内容提取存储到fb_info中并注册register_framebuffer。
static int efifb_probe(struct platform_device *dev)
{
struct fb_info *info;
int err, orientation;
unsigned int size_vmode;
unsigned int size_remap;
unsigned int size_total;
char *option = NULL;
efi_memory_desc_t md;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
return -ENODEV;
...
efifb_fix.smem_start = screen_info.lfb_base; //显存物理地址
....
info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
if (!info) {
err = -ENOMEM;
goto err_release_mem;
}
platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;
info->apertures = alloc_apertures(1);
if (!info->apertures) {
err = -ENOMEM;
goto err_release_fb;
}
info->apertures->ranges[0].base = efifb_fix.smem_start;
info->apertures->ranges[0].size = size_remap;
.... //screen_base是显存虚拟地址
if (mem_flags & EFI_MEMORY_WC)
info->screen_base = ioremap_wc(efifb_fix.smem_start,
efifb_fix.smem_len);
else if (mem_flags & EFI_MEMORY_UC)
info->screen_base = ioremap(efifb_fix.smem_start,
efifb_fix.smem_len);
else if (mem_flags & EFI_MEMORY_WT)
info->screen_base = memremap(efifb_fix.smem_start,
efifb_fix.smem_len, MEMREMAP_WT);
else if (mem_flags & EFI_MEMORY_WB)
info->screen_base = memremap(efifb_fix.smem_start,
efifb_fix.smem_len, MEMREMAP_WB);
info->fbops = &efifb_ops; //操作
....
}
efifb_ops : 相关的函数也会更新screen_base
static const struct fb_ops efifb_ops = {
.owner = THIS_MODULE,
.fb_destroy = efifb_destroy,
.fb_setcolreg = efifb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
。。。
[ 15.981146] cfb_imageblit+0x40/0x23c
[ 15.981148] fb_do_show_logo+0x68/0x1ec
[ 15.981150] fb_show_logo_line.constprop.0+0x178/0x680
[ 15.981151] fb_show_logo+0x64/0x74
[ 15.981153] fbcon_switch+0x368/0x3dc //fbdev/core/fbcon.c
[ 15.981154] redraw_screen+0x188/0x290 //tty/vt/vt.c
[ 15.981156] do_bind_con_driver.isra.0+0x2a4/0x2e0
[ 15.981158] do_take_over_console+0x68/0x70
[ 15.981159] do_fbcon_takeover.part.0+0x70/0xd8
[ 15.981161] fbcon_fb_registered+0x128/0x12c
[ 15.981162] do_register_framebuffer+0x1dc/0x33c
[ 15.981164] register_framebuffer+0x38/0x60
[ 15.981165] efifb_probe.part.0+0x51c/0x5ec
[ 15.981167] efifb_probe+0xac/0xc0
[ 15.981169] platform_drv_probe+0x60/0xc0
[ 15.981171] really_probe+0xf0/0x504
[ 15.981173] driver_probe_device+0x100/0x170
[ 15.981174] device_driver_attach+0xcc/0xd4
。。。。。
[ 55.036688] cfb_imageblit+0x40/0x23c
[ 55.036690] bit_putcs+0x244/0x474
[ 55.036692] fbcon_putcs+0x100/0x120 //fbdev/core/fbcon.c
[ 55.036693] do_update_region+0x158/0x1b0 //tty/vt/vt.c ->vc_data->consw
[ 55.036695] csi_J+0xec/0x264
[ 55.036697] do_con_trol+0xe74/0x 1110
[ 55.036699] do_con_write+0x1b0/0x4f0
[ 55.036700] con_write+0x24/0x7c
[ 55.036702] process_output_block+0xa4/0x180
[ 55.036704] n_tty_write+0x158/0x340
[ 55.036706] do_tty_write+0x12c/0x260
[ 55.036707] tty_write+0xac/0x110
[ 55.036709] vfs_write+0xfc/0x2bc
[ 55.036711] ksys_write+0x74/0x100
[ 55.036713] __arm64_sys_write+0x28/0x3c
[ 55.036715] el0_svc_common.constprop.0+0x84/0x174
。。。。
[ 1019.208612] dump_backtrace+0x0/0x1ec
[ 1019.208803] show_stack+0x24/0x6c
[ 1019.208806] dump_stack+0xd0/0x128
[ 1019.208808] cfb_imageblit+0x40/0x23c
[ 1019.208810] soft_cursor+0x140/0x200
[ 1019.209000] bit_cursor+0x314/0x52c
[ 1019.209002] fb_flashcursor+0x10c/0x130
[ 1019.209005] process_one_work+0x1d0/0x490
[ 1019.209007] worker_thread+0x188/0x520
[ 1019.209009] kthread+0x11c/0x120
[ 1019.209199] ret_from_fork+0x10/0x18
。。。
比如我们直接操作/dev/fb0;将调用drivers/video/fbdev/core/fbmem.c:fb_write ,将数据写入screen_base。
static ssize_t
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);
u8 *buffer, *src;
u8 __iomem *dst;
int c, cnt = 0, err = 0;
unsigned long total_size;
if (!info || !info->screen_base)
return -ENODEV;
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
if (info->fbops->fb_write)
return info->fbops->fb_write(info, buf, count, ppos);
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p > total_size)
return -EFBIG;
if (count > total_size) {
err = -EFBIG;
count = total_size;
}
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
count = total_size - p;
}
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
dst = (u8 __iomem *) (info->screen_base + p);
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer;
if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}
fb_memcpy_tofb(dst, src, c);
dst += c;
src += c;
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (cnt) ? cnt : err;
}
LOGO:显示Linux内核启动logo - 涛少&
一个存在efifb和drm后,vt终端没显示的问题:
由于efifb优先于drm的framebuffer创建,导致drm的没有使用上?而drm中却更新了相关寄存器,导致efifb的显卡驱动功能显存地址无效了。在使用multi-user模式下,更新寄存器的dump_stack:
[ 69.539591] ===phytium_dc_primary_plane_update //更新了drm显卡的寄存器地址
[ 69.539596] CPU: 5 PID: 310 Comm: plymouthd Tainted: G O 4.19..
0 #26
[ 69.539598] Hardware name: Lenovo Lenovo KaiTian A740J Desktop/HANGZHOU, BIOSS
WA2KT16A 08/26/22 15:22:11
[ 69.539599] Call trace:
[ 69.539606] dump_backtrace+0x0/0x178
[ 69.539607] show_stack+0x14/0x20
[ 69.539611] dump_stack+0x9c/0xbc
[ 69.539622] phytium_plane_atomic_update+0x208/0x988 [phytium_dc_drm]
[ 69.539626] drm_atomic_helper_commit_planes+0xd8/0x1f8
[ 69.539632] phytium_atomic_commit_tail+0x30/0x68 [phytium_dc_drm]
[ 69.539635] commit_tail+0x44/0x78
[ 69.539637] drm_atomic_helper_commit+0xc8/0x140
[ 69.539640] drm_atomic_commit+0x48/0x58
[ 69.539642] restore_fbdev_mode_atomic+0x1c8/0x1f8
[ 69.539644] restore_fbdev_mode+0x40/0x188
[ 69.539647] drm_fb_helper_restore_fbdev_mode_unlocked+0x70/0xd0
[ 69.539649] drm_fb_helper_lastclose+0x10/0x18
[ 69.539652] drm_lastclose+0x34/0xd8
[ 69.539654] drm_release+0xc4/0xf0
[ 69.539657] __fput+0x9c/0x210
[ 69.539659] ____fput+0xc/0x18
[ 69.539662] task_work_run+0xb4/0xe8
[ 69.539664] do_exit+0x340/0xa18
[ 69.539667] do_group_exit+0x34/0xc8
[ 69.539669] __arm64_sys_exit_group+0x14/0x18
[ 69.539671] el0_svc_common+0x84/0xd8
[ 69.539674] el0_svc_handler+0x24/0x80