ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Linux 内核 Framebuffer Aperture 所有权管理与驱动交接(aperture 辅助函数详解)

Linux 内核 Framebuffer Aperture 所有权管理与驱动交接(aperture 辅助函数详解) Linux 内核 Framebuffer Aperture 所有权管理与驱动交接aperture 辅助函数详解【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读本文围绕 Linux 内核 aperture.rst 文档所描述的 Framebuffer Aperture 所有权管理机制展开讲解图形驱动如何在启动阶段接管固件提供的帧缓冲、通用驱动EFI/VESA如何注册自身为 aperture 所有者、以及驱动间如何通过aperture_remove_conflicting_devices()系列 API 完成平滑交接。读完本文你将掌握 aperture 辅助函数的核心 API、调用时机、底层实现原理并能在自己的图形驱动 probe 函数中正确使用它们。1. 背景为什么需要管理 Framebuffer Aperture 的所有权在 Linux 启动过程中显示输出的控制权会经历多次交接固件阶段UEFI 的 GOPGraphics Output Protocol或传统 BIOS 的 VESA 模式由固件初始化此时屏幕上显示的早期启动信息由固件驱动输出。早期内核阶段内核加载通用图形驱动例如 EFI-GOP、VESA 或简单的 VGA 帧缓冲驱动继续维持屏幕输出。正式驱动阶段等到对应的硬件专用驱动如 amdgpu、i915、radeon、nouveau 等加载时需要先移除通用的早期驱动再接管硬件与帧缓冲内存。关键约束是一块图形设备可能被多个驱动支持但任意时刻只能有一个驱动处于活动状态。如果专用驱动不去除通用驱动两者会同时访问同一片显存与寄存器导致显示错乱甚至系统崩溃。Aperture孔径/窗口机制正是为这个交接过程而生的。aperture一词在此特指可被多个图形驱动寻址的帧缓冲物理内存区间framebuffer memory aperture它由物理基地址base与长度size描述。aperture 辅助函数负责跟踪谁拥有这段内存并在驱动间完成所有权交接。其核心实现位于 drivers/video/aperture.c对外接口声明于 include/linux/aperture.h。从源码结构看整个模块由一个全局链表apertures与一把互斥锁apertures_lock保护见 drivers/video/aperture.c#L141-L142每个已注册的区间对应一个struct aperture_range节点记录所属设备、基地址、大小与 detach 回调struct aperture_range { struct device *dev; resource_size_t base; resource_size_t size; struct list_head lh; void (*detach)(struct device *dev); };2. 内核配置CONFIG_APERTURE_HELPERSaperture 辅助函数由内核配置项CONFIG_APERTURE_HELPERS控制定义于 drivers/video/Kconfig#L8-L12config APERTURE_HELPERS bool help Support tracking and hand-over of aperture ownership. Required by graphics drivers for firmware-provided framebuffers.它由图形驱动通过select APERTURE_HELPERS间接开启隐藏选项不可手动配置。当该选项未开启时include/linux/aperture.h#L23-L44 提供了一组返回0的static inline空实现保证调用方代码无需#ifdef即可编译体现了内核 API 向后兼容的常规做法。3. 核心 API 全景aperture 辅助函数共包含 4 个对外接口全部声明在 include/linux/aperture.h实现在 drivers/video/aperture.cAPI功能典型调用方aperture_remove_conflicting_devices(base, size, name)移除指定物理区间内的冲突设备平台总线图形驱动aperture_remove_conflicting_pci_devices(pdev, name)移除 PCI 设备所有 BAR 区间内的冲突设备自动探测 aperturePCI 图形驱动ast、bochs、vboxvideo 等aperture_remove_all_conflicting_devices(name)移除所有已知冲突设备整段物理内存帧缓冲位置不固定的驱动devm_aperture_acquire_for_platform_device(pdev, base, size)以平台设备身份注册为 aperture 所有者固件通用驱动simpledrm、efidrm、ofdrm、vesadrm 等3.1 移除类 API 的内部实现三个“移除”类 API 最终都收敛到同一个内部函数aperture_detach_devices(base, size)drivers/video/aperture.c#L247-L271static void aperture_detach_devices(resource_size_t base, resource_size_t size) { resource_size_t end base size; struct list_head *pos, *n; mutex_lock(apertures_lock); list_for_each_safe(pos, n, apertures) { struct aperture_range *ap container_of(pos, struct aperture_range, lh); struct device *dev ap-dev; if (WARN_ON_ONCE(!dev)) continue; if (!overlap(base, end, ap-base, ap-base ap-size)) continue; ap-dev NULL; /* detach from device */ list_del(ap-lh); ap-detach(dev); } mutex_unlock(apertures_lock); }其工作流程为遍历全局 aperture 链表用区间重叠判断overlap(base1, end1, base2, end2)drivers/video/aperture.c#L144-L148即(base1 end2) (end1 base2)筛选出与目标区间相交的所有注册者将匹配节点从链表摘除并调用其detach回调完成设备解绑。对于平台设备detach 回调是aperture_detach_platform_device()drivers/video/aperture.c#L200-L217其核心是调用platform_device_unregister(pdev)将固件帧缓冲设备从设备层级中注销。这正是“热拔出”hot-unplug语义的落地固件驱动的设备被注销后其驱动自然卸载专用驱动随即接管硬件。3.2 获取类 API 的内部实现devm_aperture_acquire_for_platform_device()只是devm_aperture_acquire()drivers/video/aperture.c#L163-L198在平台设备场景下的封装传入aperture_detach_platform_device作为 detach 回调。其核心逻辑加锁遍历链表若目标区间与任一已注册区间重叠立即返回-EBUSY否则分配struct aperture_range节点填入设备、基地址、大小加入全局链表通过devm_add_action_or_reset()注册释放动作设备销毁时自动从链表移除无需驱动手动清理。这里体现了“devm”前缀的托管语义所有成功获取的 aperture 在底层设备消失时自动释放。4. 实战一专用驱动如何接管设备移除冲突驱动4.1 平台设备场景文档给出的平台设备示例完整代码见 drivers/video/aperture.c#L34-L59展示了标准用法在 probe 函数最顶部调用aperture_remove_conflicting_devices()static int example_probe(struct platform_device *pdev) { struct resource *mem; resource_size_t base, size; int ret; mem platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -ENODEV; base mem-start; size resource_size(mem); ret aperture_remove_conflicting_devices(base, size, example); if (ret) return ret; // Initialize the hardware ... return 0; } static const struct platform_driver example_driver { .probe example_probe, ... };要点分析参数base/size来自平台设备实例的 I/O 内存资源IORESOURCE_MEM活动帧缓冲必然位于该区间内参数name是请求方驱动的描述性名称用于日志与调试调用成功后之前声明拥有该区间、正在驱动帧缓冲输出的驱动会被释放新驱动即可安全接管。值得注意的是该函数还会调用sysfb_disable(NULL)drivers/video/aperture.c#L296。从源码注释drivers/video/aperture.c#L287-L295看这是为了阻止 sysfb 后续再注册固件帧缓冲设备——既然已有真实驱动接管显示设备就不应再让系统固件创建新的通用帧缓冲设备。4.2 PCI 设备场景PCI 驱动可以调用aperture_remove_conflicting_pci_devices(pdev, name)drivers/video/aperture.c#L347-L373让函数自动遍历设备所有 BARPCI_STD_NUM_BARS个标准 BAR中类型为IORESOURCE_MEM的资源区间逐一调用aperture_detach_devices()。该函数还有两个额外的细节若该 PCI 设备是主显示适配器pdev vga_default_device()还会额外调用__aperture_remove_legacy_vga_devices()drivers/video/aperture.c#L325-L333它负责移除位于固定物理地址VGA_FB_PHYS_BASE、大小VGA_FB_PHYS_SIZE的传统 VGA 帧缓冲调用vga_remove_vgacon(pdev)移除 VGA 文本模式控制台vgacon。函数注释明确警告调用前必须先移除图形驱动否则在启用 vgacon 时 vga fbdev 驱动会崩溃。启动时调用sysfb_disable(pdev-dev)语义与平台版本一致。仓库中有大量真实调用实例例如drivers/gpu/drm/ast/ast_drv.c#L383ret aperture_remove_conflicting_pci_devices(pdev, ast_driver.name);drivers/gpu/drm/tiny/bochs.c#L753ret aperture_remove_conflicting_pci_devices(pdev, bochs_driver.name);drivers/gpu/drm/vboxvideo/vbox_drv.c#L49ret aperture_remove_conflicting_pci_devices(pdev, driver.name);三个驱动均将调用放在 probe 的最前面紧跟其后才做设备使能与 DRM 设备分配。4.3 帧缓冲位置未知的场景对于帧缓冲可能位于物理内存任意位置的驱动可以使用aperture_remove_all_conflicting_devices(name)include/linux/aperture.h#L57-L60。它本质上是aperture_remove_conflicting_devices(0, (resource_size_t)-1, name)的内联封装——即以0到(resource_size_t)-1覆盖整个物理地址空间从而移除所有已知的冲突设备。5. 实战二通用驱动如何注册为所有者可被强制移除固件提供的通用驱动EFI、VESA 等位于交接链的“被移除方”它们必须通过devm_aperture_acquire_for_platform_device()注册自己对帧缓冲 aperture 的所有权。只有成功注册为所有者后专用驱动调用移除类 API 时才会触发它们的卸载。5.1 文档示例static int generic_probe(struct platform_device *pdev) { struct resource *mem; resource_size_t base, size; mem platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -ENODEV; base mem-start; size resource_size(mem); ret devm_aperture_acquire_for_platform_device(pdev, base, size); if (ret) return ret; // Initialize the hardware ... return 0; } static int generic_remove(struct platform_device *) { // Hot-unplug the device ... return 0; } static const struct platform_driver generic_driver { .probe generic_probe, .remove generic_remove, ... };5.2 关键约束失败条件若目标区间或其中一部分已被其他驱动拥有函数返回-EBUSYprobe 失败热拔插前提注册成功后该驱动就处于“可被强制移除”状态。但这只对支持热拔插的平台驱动有效——当其他驱动调用aperture_remove_conflicting_devices()等函数时aperture 助手会调用platform_device_unregister()通用驱动随之卸载。因此通用驱动必须提供remove函数否则交接无法完成见 drivers/video/aperture.c#L123-L130 的文档注释卸载后的行为边界一旦从硬件上被热拔出驱动之后不得再访问设备的寄存器、帧缓冲内存、ROM 等资源。5.3 仓库真实实现simpledrm 与 efidrmsysfb 子系统中的固件帧缓冲 DRM 驱动正是这一模式的典型实现drivers/gpu/drm/sysfb/simpledrm.c#L750-L755ret devm_aperture_acquire_for_platform_device(pdev, mem-start, resource_size(mem)); if (ret) { drm_err(dev, could not acquire memory range %pr: %d\n, mem, ret); return ERR_PTR(ret); }drivers/gpu/drm/sysfb/efidrm.c#L228-L232ret devm_aperture_acquire_for_platform_device(pdev, res-start, vsize); if (ret) { drm_err(dev, could not acquire memory range %pr: %d\n, res, ret); return ERR_PTR(ret); }同样调用模式的还包括 sysfb 目录下的 ofdrm、vesadrm、corebootdrm 驱动它们共同构成了“固件通用驱动注册所有权 → 专用驱动移除冲突 → 通用驱动卸载”的完整交接链。从源码结构看platform_device_unregister()之后固件驱动的设备状态会丢失因此固件驱动在被移除后不能再继续驱动显示输出。6. 完整的驱动交接流程将前两节串联起来一次典型的显示驱动交接按以下顺序发生启动早期 专用驱动加载时 ───────────────────────── ───────────────────────────── 固件初始化显示输出 → 专用驱动 probe() 开始 ↓ ↓ sysfb 注册平台设备 → aperture_remove_conflicting_pci_devices() simpledrm/efidrm 等 ↓ ↓ aperture_detach_devices() 发现重叠区间 通用驱动 probe() ↓ ↓ platform_device_unregister() 注销固件设备 devm_aperture_acquire_ ↓ for_platform_device() 通用驱动 remove() 执行硬件停止输出 注册为所有者 ↓ ↓ 专用驱动初始化硬件接管帧缓冲 系统进入正常工作状态核心不变量是任何时刻一个帧缓冲区间至多只有一个所有者。获取类 API 用-EBUSY保证互斥移除类 API 用链表遍历 区间重叠检测 platform_device_unregister()完成强制定向交接。7. 使用建议与注意事项综合文档与源码编写图形驱动时请遵循以下实践调用位置在 probe 函数最顶部、任何硬件初始化之前调用移除类 API避免先访问硬件再被其他驱动干扰参数来源平台设备优先从IORESOURCE_MEM资源读取base/sizePCI 设备直接传pdev即可函数自动遍历 BAR帧缓冲位置未知时使用aperture_remove_all_conflicting_devices()代价是移除所有已知冲突设备通用驱动必须实现remove否则无法被热拔出交接链断裂托管语义所有注册的 aperture 均随底层设备生命周期自动释放无需手动清理但要注意获取失败-EBUSY时应立即中止 probe传统 VGAPCI 主适配器会自动清理 VGA 帧缓冲与 vgacon无需手动处理若硬件帧缓冲位于非 BAR 位置可先获取所有权再调用__aperture_remove_legacy_vga_devices()手动释放剩余 VGA 设备drivers/video/aperture.c#L304-L324。8. 延伸阅读文档原文Documentation/driver-api/aperture.rst核心实现drivers/video/aperture.c对外接口声明include/linux/aperture.h配置项drivers/video/Kconfig#L8-L12调用方示例simpledrmdrivers/gpu/drm/sysfb/simpledrm.c、efidrmdrivers/gpu/drm/sysfb/efidrm.c、astdrivers/gpu/drm/ast/ast_drv.c、bochsdrivers/gpu/drm/tiny/bochs.c、vboxvideodrivers/gpu/drm/vboxvideo/vbox_drv.c【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表