4. Linux 驱动基础回顾:字符设备驱动框架、platform驱动、设备树匹配机制
在将驱动从 Linux 移植到 TrustZone 安全环境之前,必须牢固掌握 Linux 内核驱动的三大核心基础:字符设备驱动框架、platform 驱动模型以及设备树(Device Tree)匹配机制。这三者构成了 Linux 驱动与硬件解耦、动态匹配的基石,也是安全驱动移植中需要重新实现或适配的关键部分。
核心观点:Linux 驱动通过“设备-总线-驱动”三层模型实现硬件与驱动的分离;设备树提供硬件描述,platform 总线负责匹配,字符设备框架提供用户空间访问接口。TrustZone 环境通常需要将这三层逻辑重新实现在安全世界(Secure World)中。
4.1 字符设备驱动框架
字符设备是 Linux 中最基本的设备类型,以字节流方式访问,典型代表包括串口、GPIO、I2C 控制器等。字符设备驱动的核心是 struct file_operations 结构体,它定义了用户空间通过系统调用(open、read、write、ioctl 等)访问设备的具体实现。
4.1.1 核心数据结构
| 数据结构 | 作用 | 关键字段 |
|---|---|---|
struct cdev |
内核中表示字符设备的对象 | ops(file_operations 指针)、dev(设备号) |
struct file_operations |
定义设备操作函数表 | open、release、read、write、unlocked_ioctl |
dev_t |
设备号(主设备号 + 次设备号) | MAJOR(dev)、MINOR(dev) |
4.1.2 字符设备注册流程
- 分配设备号:使用
alloc_chrdev_region()动态分配,或register_chrdev_region()静态指定。 - 初始化 cdev:调用
cdev_init()绑定file_operations。 - 添加设备:调用
cdev_add()将设备注册到内核。 - 创建设备节点:通过
device_create()在 sysfs 中生成设备文件。
// 字符设备驱动典型框架代码
static int __init my_driver_init(void)
{
dev_t dev_num;
struct device *dev;
// 1. 分配设备号
alloc_chrdev_region(&dev_num, 0, 1, "my_device");
// 2. 初始化 cdev
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;
// 3. 添加设备
cdev_add(&my_cdev, dev_num, 1);
// 4. 创建设备节点
dev = device_create(my_class, NULL, dev_num, NULL, "my_device");
return 0;
}
module_init(my_driver_init);
移植要点:在 TrustZone 安全世界中,没有 Linux 的 VFS 层和 sysfs。字符设备的“设备节点”概念需要替换为安全监控调用(SMC)或共享内存通信机制。但
file_operations 中的操作语义(如 read/write/ioctl)可以直接映射为安全服务的 API 接口。
4.2 platform 驱动模型
platform 驱动是 Linux 内核中用于管理非即插即用设备(如 SoC 内部外设、中断控制器、GPIO 控制器)的虚拟总线模型。它通过 platform_driver 和 platform_device 的匹配机制,将驱动代码与硬件描述分离。
4.2.1 核心数据结构
| 结构体 | 职责 | 关键字段 |
|---|---|---|
struct platform_driver |
描述驱动逻辑,包含 probe/remove 回调 | driver.name、probe、remove、id_table |
struct platform_device |
描述设备硬件资源(内存、中断、DMA) | name、id、num_resources、resource |
struct resource |
描述单个硬件资源(IO 地址、中断号) | start、end、flags(IORESOURCE_MEM/IRQ) |
4.2.2 匹配机制
platform 总线通过以下顺序匹配设备和驱动:
- 名称匹配:比较
platform_driver.driver.name与platform_device.name。 - ID 表匹配:通过
platform_driver.id_table(struct of_device_id数组)进行设备树兼容性匹配。 - ACPI 匹配:在支持 ACPI 的平台上使用 ACPI 表匹配。
// platform 驱动典型框架
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-device", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static int my_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *base;
// 获取 IO 内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
// 获取中断资源
int irq = platform_get_irq(pdev, 0);
return 0;
}
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_device",
.of_match_table = my_of_match,
},
};
module_platform_driver(my_driver);
移植陷阱:在 TrustZone 安全世界中,没有 platform 总线机制。驱动需要直接操作硬件寄存器,无法依赖
platform_get_resource 等 API。移植时需要将资源获取逻辑改为直接访问安全内存映射区域,并通过安全监控器(Monitor)管理中断。
4.3 设备树(Device Tree)匹配机制
设备树是一种描述硬件拓扑结构的数据结构,以 .dts 文件形式存在,编译为 .dtb 二进制文件后由 bootloader 传递给内核。设备树的核心作用是让内核无需硬编码硬件信息,通过解析树状节点自动发现设备。
4.3.1 设备树关键语法
| 语法元素 | 示例 | 说明 |
|---|---|---|
compatible |
"vendor,device" |
驱动匹配的核心标识,格式为“厂商,设备型号” |
reg |
<0x10000000 0x1000> |
描述设备地址空间(起始地址 + 长度) |
interrupts |
<0 31 4> |
描述中断号、触发类型等 |
status |
"okay" / "disabled" |
控制设备是否启用 |
4.3.2 匹配流程
当内核启动时,设备树解析流程如下:
- Bootloader 加载 DTB:将设备树二进制文件放入内存,传递地址给内核。
- 内核解析 DTB:调用
unflatten_device_tree()将扁平设备树转换为树状结构。 - 设备注册:遍历设备树节点,为每个
compatible属性匹配的节点创建platform_device。 - 驱动匹配:platform 总线根据
of_match_table查找匹配的驱动,调用probe()。
// 设备树节点示例(.dts 文件)
/ {
soc {
my_device: my-device@10000000 {
compatible = "vendor,my-device";
reg = <0x10000000 0x1000>;
interrupts = <0 31 4>;
status = "okay";
};
};
};
// 驱动中对应的 of_match_table
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-device", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
移植要点:在 TrustZone 环境中,设备树通常由安全启动固件(如 OP-TEE 的 DTS)管理。安全驱动需要从安全设备树中解析硬件资源,但解析逻辑必须精简,避免引入 Linux 庞大的设备树库。常见做法是使用扁平设备树(FDT)的轻量级解析库,如
libfdt。
4.4 三者的关系与移植映射
字符设备、platform 驱动、设备树三者协同工作,构成了 Linux 驱动从硬件描述到用户空间访问的完整链路。下表总结了它们在 Linux 中的角色以及在 TrustZone 环境中的移植映射:
| 组件 | Linux 角色 | TrustZone 移植映射 |
|---|---|---|
| 设备树 | 硬件拓扑描述,提供资源信息 | 使用安全固件 DTS 或硬编码资源表 |
| platform 驱动 | 设备-驱动匹配与资源获取 | 替换为安全世界的手动资源绑定 |
| 字符设备框架 | 用户空间访问接口 | 替换为 SMC 调用或共享内存通信 |
| file_operations | 系统调用到驱动的映射 | 映射为安全服务的函数指针表 |