内核efifb的显示功能

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 - 涛少& - 博客园 (cnblogs.com)

一个存在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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

古井无波 2024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值