0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入解析Linux内核debug_kinfo驱动:为Bootloader打造的内核信息备份方案

jf_44130326 来源:Linux 作者:Linux 2026-03-12 08:11 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

Android/GKI(Generic Kernel Image)等基于Linux内核的系统中,bootloader往往需要获取内核的关键信息以完成调试、启动校验等操作。debug_kinfo驱动正是为解决这一需求而生——它将内核符号表、内存布局、编译配置等核心信息封装并写入预留内存区域,供bootloader直接读取。本文将从功能定位、代码流程、核心设计三个维度,深度剖析debug_kinfo的实现逻辑,并通过流程图直观呈现其核心执行路径。

一、debug_kinfo的核心功能

debug_kinfo驱动的核心目标是标准化内核关键信息的存储与传递,具体实现了以下核心能力:

1.收集内核编译配置(如KALLSYMS、CFI_CLANG等开关)、符号表元数据(符号数量、地址表物理地址等);

2.记录内核内存布局(如_stext/_etext物理地址、模块内存区间等);

3.提供用户态接口,支持动态写入build信息(如版本号、编译时间);

4.将所有信息写入设备树指定的预留内存区域,保证bootloader可访问;

5.生成校验和,确保信息完整性。

该驱动主要依赖Linux平台总线(platform_driver)和预留内存(reserved_mem)机制实现,适配了设备树(DTB)驱动模型,具备良好的可移植性。

二、核心数据结构:信息封装的载体

要理解debug_kinfo的工作逻辑,首先需掌握其定义的两个核心结构体(位于debug_kinfo.h):

1. kernel_info:内核信息本体

structkernel_info {// Kallsyms相关编译配置__u8 enabled_all;        // 是否开启CONFIG_KALLSYMS_ALL__u8 enabled_base_relative;   // 是否开启CONFIG_KALLSYMS_BASE_RELATIVE__u8 enabled_absolute_percpu;  // 是否开启CONFIG_KALLSYMS_ABSOLUTE_PERCPU__u8 enabled_cfi_clang;     // 是否开启CONFIG_CFI_CLANG// 符号表元数据__u32 num_syms;         // 符号总数(kallsyms_num_syms)__u16 name_len;         // 符号名最大长度(KSYM_NAME_LEN)__u16 bit_per_long;       // 内核long类型位数(BITS_PER_LONG)// 物理地址信息(供bootloader寻址)__u64 _addresses_pa;      // kallsyms_addresses物理地址__u64 _relative_pa;       // kallsyms_relative_base物理地址__u64 _stext_pa;        // 内核文本段起始物理地址__u64 _etext_pa;        // 内核文本段结束物理地址// 其他关键信息:线程栈大小、swapper页表物理地址、build信息、模块布局等__u32 thread_size;       // 线程栈大小(THREAD_SIZE)__u8 build_info[BUILD_INFO_LEN];// 构建信息(256字节)__u64 module_start_va;     // 模块虚拟地址起始__u64 module_end_va;      // 模块虚拟地址结束} __packed;

关键设计:使用__packed属性强制按字节对齐,避免不同架构下的内存对齐差异导致bootloader解析出错。

2. kernel_all_info:带校验的完整信息包

structkernel_all_info {__u32 magic_number;       // 魔数(0xCCEEDDFF),用于合法性校验__u32 combined_checksum;    // 校验和(异或校验)structkernel_info info;    // 内核核心信息} __packed;

魔数用于bootloader识别有效信息区域,校验和则通过异或运算生成,确保信息未被篡改。

三、代码执行流程:从驱动加载到信息写入

debug_kinfo的核心逻辑集中在debug_kinfo.c,整体流程可分为驱动初始化(probe)信息填充用户态接口三个阶段。为了更直观理解,先呈现整体执行流程图:

wKgZPGmyBYeAVCqDAANgVRY9JCA047.png

阶段1:驱动probe——绑定设备并初始化内存

作为platform_driver,debug_kinfo的入口是debug_kinfo_probe函数,负责完成设备绑定、预留内存校验、信息初始化:

步骤1:解析设备树,获取预留内存区域

mem_region= of_parse_phandle(pdev->dev.of_node,"memory-region",0);rmem= of_reserved_mem_lookup(mem_region);

驱动从设备树节点中读取memory-region属性,找到预留给bootloader的内存区域(reserved_mem)。该内存区域由内核提前分配且不参与普通内存管理,确保bootloader可直接访问。

步骤2:校验预留内存合法性

if(!rmem->base|| !rmem->size)return-EINVAL;if(rmem->size < sizeof(struct kernel_all_info)) return -EINVAL;

检查预留内存的基地址、大小是否有效,且需至少容纳kernel_all_info结构体(避免信息截断)。

步骤3:映射预留内存并初始化

all_info_addr = rmem->priv; // 获取预留内存的虚拟地址memset(all_info_addr,0,sizeof(structkernel_all_info));// 清空内存

rmem->priv是预留内存的虚拟地址(内核已完成映射),驱动先清空该区域,为后续填充信息做准备。

阶段2:填充内核核心信息

probe函数的核心逻辑是将内核关键信息写入kernel_info结构体,对应流程图中I-L步骤,主要分为以下几类:

1.编译配置信息(宏定义展开)

info->enabled_all = IS_ENABLED(CONFIG_KALLSYMS_ALL);info->enabled_cfi_clang = IS_ENABLED(CONFIG_CFI_CLANG);

通过IS_ENABLED宏读取内核编译时的配置(如是否开启符号表、CFI校验等),将布尔值转为u8类型存储。

2.符号表元数据(kallsyms相关)

info->num_syms = kallsyms_num_syms; // 符号总数if (!info->enabled_base_relative) {info->_addresses_pa = (u64)__pa_symbol(kallsyms_addresses); // 物理地址} else {info->_relative_pa = (u64)__pa_symbol(kallsyms_relative_base);info->_offsets_pa = (u64)__pa_symbol(kallsyms_offsets);}

•kallsyms_addresses:内核符号地址表,__pa_symbol将虚拟地址转为物理地址(供bootloader访问);

•若开启CONFIG_KALLSYMS_BASE_RELATIVE,则存储相对基地址和偏移量表,而非绝对地址表。

3.内核内存布局信息

info->_stext_pa = (u64)__pa_symbol(_stext); // 文本段起始物理地址info->_etext_pa = (u64)__pa_symbol(_etext); // 文本段结束物理地址info->_end_pa = (u64)__pa_symbol(_end);   // 内核镜像结束物理地址info->swapper_pg_dir_pa = (u64)__pa_symbol(swapper_pg_dir);// 页表物理地址

记录内核核心段的物理地址,bootloader可通过这些地址定位内核镜像位置。

4.模块相关信息

#ifdefined(CONFIG_RANDOMIZE_BASE) && defined(MODULES_VSIZE)info->module_start_va = module_alloc_base;info->module_end_va = info->module_start_va + MODULES_VSIZE;#elifdefined(CONFIG_MODULES) && defined(MODULES_VADDR)info->module_start_va = MODULES_VADDR;info->module_end_va = MODULES_END;#elseinfo->module_start_va = VMALLOC_START;info->module_end_va = VMALLOC_END;#endif

根据内核配置,动态设置模块的虚拟地址区间,适配不同的模块加载策略(如地址随机化、固定地址)。

5.生成校验和

update_kernel_all_info(all_info);

调用update_kernel_all_info函数,通过异或运算计算kernel_info所有u32字段的校验和,存入combined_checksum,同时设置魔数DEBUG_KINFO_MAGIC。

阶段3:用户态接口——动态写入build信息

驱动提供了一个模块参数build_info,支持用户态动态写入构建信息(如版本号、编译时间),对应流程图中O-S步骤:

1.定义参数操作接口

staticconststructkernel_param_opsbuild_info_op = {.set = build_info_set, // 设置函数};module_param_cb(build_info, &build_info_op,NULL,0200);

module_param_cb注册一个回调型模块参数,权限0200表示只有root可写;用户通过echo "build-info" > /sys/module/debug_kinfo/parameters/build_info即可写入。

2.实现参数设置逻辑

staticintbuild_info_set(constchar*str,conststructkernel_param *kp){all_info = (structkernel_all_info *)all_info_addr;// 拷贝build信息(截断过长内容)memcpy(&all_info->info.build_info, str,min(build_info_size -1,strlen(str)));update_kernel_all_info(all_info);// 重新计算校验和// 过长时打印警告并返回错误if(strlen(str) > build_info_size) {pr_warn("Build info buffer can't hold entire stringn");return-ENOMEM;}return0;}

写入build信息后,驱动会重新计算校验和,确保bootloader读取的信息完整性。

四、设计亮点与工程价值

1.跨阶段通信的标准化

通过预留内存+固定结构体的方式,解决了内核与bootloader之间的信息传递问题——无需修改bootloader核心逻辑,只需按结构体解析即可获取内核信息。

2.兼容性与可移植性

•基于platform_driver和设备树,适配不同硬件平台;

•使用__packed对齐、__pa_symbol地址转换等内核通用接口,兼容不同架构(ARM/ARM64/x86);

•条件编译适配不同内核配置(如模块地址随机化、KALLSYMS_BASE_RELATIVE)。

3.安全性与完整性

•魔数校验:bootloader可通过magic_number快速识别有效信息区域;

•异或校验和:防止内存数据被篡改,保证信息可信度。

4.可扩展性

•build_info字段支持动态写入,适配不同场景的自定义信息需求;

•kernel_info结构体预留了扩展字段(如模块布局偏移、percpu配置),可按需添加新信息。

五、总结

关键点回顾

1.debug_kinfo驱动核心是通过预留内存+标准化结构体,实现内核向bootloader传递关键信息,核心流程为“解析设备树→校验内存→填充信息→生成校验→提供用户态接口”;

2.核心设计亮点包括__packed对齐保证跨架构兼容、魔数+校验和保证信息完整性、模块参数支持动态写入build信息;

3.该驱动是内核与bootloader跨阶段通信的典型实现,为嵌入式系统调试、启动校验提供了标准化方案。

debug_kinfo驱动是Linux内核与bootloader之间的“信息桥梁”,其核心设计思路是将内核运行时的关键元数据标准化、物理化存储,解决了跨执行阶段的信息传递难题。从代码实现来看,它充分利用了Linux内核的platform_driver、reserved_mem、模块参数等机制,兼顾了兼容性、安全性和可扩展性。

对于嵌入式系统开发者而言,理解debug_kinfo的实现逻辑,不仅能掌握内核信息封装的技巧,还能为定制化调试工具、bootloader适配提供参考——比如基于该驱动扩展更多内核状态信息,或优化bootloader的内核信息解析逻辑,提升系统调试和启动的可靠性。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11807

    浏览量

    219512
  • bootloader
    +关注

    关注

    2

    文章

    245

    浏览量

    48285
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何理解Linux内核中的PCIe驱动

    我们习惯了用 Verilog 去死磕 PCIe 的底层协议状态机。但一旦越过硬件边界来到操作系统层面,Linux 内核是如何接管并驱动这些 PCI/PCIe 设备的呢?由于不同的 CPU 架构实现了
    的头像 发表于 04-11 17:22 1234次阅读

    Linux内核驱动开发的技术核心精要

    嵌入式Linux驱动开发是连接硬件与操作系统的关键环节。随着内核演进(如Linux 6.13)和硬件复杂度提升,开发者需掌握并发控制、中断分层、内存管理、设备树、调试工具等核心知识。本
    发表于 03-10 13:56

    深入RK3588内核:rockchip_linux_defconfig的作用与调试价值

    在 RK3588 芯片的 Linux 开发中,有一个文件始终是开发者绕不开的核心 ——kernel/arch/arm64/configs/rockchip_linux_defconfig。无论是首次
    的头像 发表于 02-03 15:56 1357次阅读
    <b class='flag-5'>深入</b>RK3588<b class='flag-5'>内核</b>:rockchip_<b class='flag-5'>linux</b>_defconfig的作用与调试价值

    深入解析rk平台Android Bootloader核心代码:从启动流程到AVB验证

    作为Android设备启动的第一道“闸门”,Bootloader(以U-Boot为主)承担着初始化硬件、加载内核、验证镜像完整性的核心职责。今天我们拆解Rockchip平台
    的头像 发表于 01-22 07:06 466次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>解析</b>rk平台Android <b class='flag-5'>Bootloader</b>核心代码:从启动流程到AVB验证

    【「Linux 设备驱动开发(第 2 版)」阅读体验】+读深入理解Linux内核内存分配

    每个内存地址是虚拟的,不是直接指向RAM中的任何地址。当用户访问内存中的存储单元时,都会进行地址转换以匹配相应的物理内存。书籍的第10章讨论了五个主题,对Linux内核内存分配进行详细讲解。 接着
    发表于 01-16 20:05

    【「Linux 设备驱动开发(第 2 版)」阅读体验】Linux内核开发基础

    感谢电子发烧友论坛提供的《Linux设备驱动开发(第2版)》阅读机会,测评将从Linux内核开发基础、Linux
    发表于 01-12 22:45

    【「Linux 设备驱动开发(第 2 版)」阅读体验】+读内核处理的核心辅助函数

    上周收到《Linux 设备驱动开发(第 2 版)》书籍,这是一本介绍Linux内核开发的指导性书籍。全面了解Linux
    发表于 01-10 22:08

    深入解析RK平台Android/Linux Bootloader核心文件:android_bootloader.c

    Bootloader是Android设备启动的第一道“关卡”,负责初始化硬件、加载系统镜像并完成内核启动的前置准备。在基于U-Boot的Android设备中,android_bootloader
    的头像 发表于 01-09 10:58 1401次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>解析</b>RK平台Android/<b class='flag-5'>Linux</b> <b class='flag-5'>Bootloader</b>核心文件:android_<b class='flag-5'>bootloader</b>.c

    深入Linux内核:进程调度的核心逻辑与实现细节

    ,背后都离不开内核调度算法的精准操控。今天,我们就从优先级、调度算法、时间片分配到底层实现,全方位拆解Linux内核进程调度的核心逻辑。 一、进程调度的“身份标识”:优先级与分类 要理解调度逻辑,首先得搞懂:进程凭什么“插队”?
    的头像 发表于 12-24 07:05 4558次阅读
    <b class='flag-5'>深入</b><b class='flag-5'>Linux</b><b class='flag-5'>内核</b>:进程调度的核心逻辑与实现细节

    Linux内核日志玩明白了吗?printk调试神器全解析

    前言:做Linux驱动开发或内核调试的朋友,一定对printk不陌生,但你真的会用它吗?为什么同样是调试RK3588内核,别人能精准捕捉关键错误,你却被海量日志淹没?今天就带大家吃透p
    的头像 发表于 12-19 08:32 1048次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>日志玩明白了吗?printk调试神器全<b class='flag-5'>解析</b>

    Linux内核模块的加载机制

    ,比如当插入一个新设备时,udev会根据设备信息自动加载对应的驱动模块。这是通过uevent事件和用户空间的工具配合实现的,提高了设备的即插即用能力。 3、解析加载.ko文件 .ko文件
    发表于 11-25 06:59

    【迅工业RK3568稳定可靠】itop-3568开发板Linux驱动开发实战:RK3568内核模块符号导出详解

    【迅工业RK3568稳定可靠】itop-3568开发板Linux驱动开发实战:RK3568内核模块符号导出详解
    的头像 发表于 11-21 13:25 1585次阅读
    【迅<b class='flag-5'>为</b>工业RK3568稳定可靠】itop-3568开发板<b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>开发实战:RK3568<b class='flag-5'>内核</b>模块符号导出详解

    Linux内核printk日志级别全解析:从参数解读到实操配置

    ,避免被无效信息淹没。 二、先搞懂:什么是 printk 输出等级? printk 是 Linux 内核
    的头像 发表于 11-20 15:54 1937次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>printk日志级别全<b class='flag-5'>解析</b>:从参数解读到实操配置

    Linux内核参数调优方案

    在高并发微服务环境中,网络性能往往成为K8s集群的瓶颈。本文将深入探讨如何通过精细化的Linux内核参数调优,让你的K8s节点网络性能提升30%以上。
    的头像 发表于 08-06 17:50 1143次阅读

    RK3568开发板内核模块实现-查看模块信息

    驱动模块加载之后,使用“modinfo helloworld.ko”命令可以获得模块的信息,包括模块作者,模块说明,模块支持的参数等等。 lsmod 命令可以列出已经载入 Linux 内核
    发表于 05-16 11:18