7.9 KiB
Haiku:device_node 树 / 内核对象模型 / KDL / BeOS 兼容边界
Haiku 有**两套容易看错的东西**,而且**逆驱动与逆系统组件的入口完全不同**:驱动侧:device_node 树 + on-demand exploration + driver module / device module 两层
系统侧:team / thread / area / port / semaphore 这套内核对象模型 + 用户态服务
如果目标不是驱动,别继续在 Device Manager 里找——换到内核对象模型与用户态服务那一层。 </CORE RULE>
一、驱动侧:device_node 树与按需发现
- 设备管理器围绕 device_node 构建:每个驱动发布一个或多个节点,构成一棵动态表示硬件的树
- 一个 device_node = module + attributes + resources + 父/子指针;module 是唯一必需的部分
- 启动时只有根节点;主要总线注册上去;智能总线为自己发现的每个设备创建子节点
- 按需探索:需要某功能时系统扫描设备文件系统,按内建规则把设备路径翻译成 device node(智能总线上的节点带 PCI 式类型信息),只查询匹配的模块目录
boot 时驱动没出现、后来访问某设备时才出现
→ 可能只是 on-demand exploration
- 有"只在系统要求时才被搜索"的按需标志
二、驱动侧:两层模块(driver module / device module)
driver module(名字必须以 driver_v1 结尾)实现:supports_device、register_device、init_driver/uninit_driver、register_child_devices、rescan_child_devices、device_removed、suspend/resume(后两个从不被调用)
register_device注册节点并可认领 I/O 资源(端口或内存范围)——同一资源只能被一个节点认领
device module(名字必须以 device_v1 结尾,经 publish 一类调用发布到 /dev)导出:init_device/uninit_device/device_removed、open/close/free/read/write/io/control/select/deselect
在 driver module 里找不到 read/write 很正常——那是 device module 的接口。
- 节点属性含显示名、唯一 ID、固定子节点、厂商/设备/类型/子类型/接口、标志
- 固定子节点与动态子节点不能混用,注册时机也不同(固定在前)
- 较新版本给节点加了"路径/驱动名"属性,用于显示某设备由哪个驱动、走哪条
/dev路径
三、系统侧:内核对象模型
逆一个非驱动的目标时,入口在这里:
| 对象 | 说明 |
|---|---|
| team | 进程等价物:地址空间与资源容器 |
| thread | 执行单位,隶属 team |
| area | 内存区域(可共享/映射),是"内存从哪来"的答案 |
| port | 内核消息队列——线程间、team 间通信的基本手段 |
| semaphore | 同步原语 |
- 信号量的行为有若干标志位影响:可中断(即使调用方没要求,也可能被终止信号打断)、释放全部等待者、只在有等待者时才递减——后两者正是实现条件变量语义所需要的
- 调试 API 与这些对象一一对应:调试器侧有表示 area / semaphore / image / team / thread / syscall 信息的模型类
逆向含义:定位"这块内存在哪、谁在等谁、谁给谁发消息",答案在 area / port / semaphore,而不是驱动框架。
四、KDL 与内核调试接口
KDL 是内建在内核里的交互式命令调试器,进入方式包括内核 panic、显式的调试器调用、紧急组合键、以及硬件异常(缺页、保护违规)。
常用命令:帮助、栈回溯、继续执行、线程列表、切换 CPU、dump 系统日志、查内存、重启。
- 命令系统可扩展:各内核子系统可以注册自己的命令
- 相关输出机制各有适用上下文(在内核调试器上下文里用的打印与常规内核打印不同)
- 调试 API 的细节对分析很关键:
- 发给调试器的消息以一个含线程与 team ID 的头结构开头,后来又加了调试端点端口字段,使同步调试消息能带上调试通道
- 存在一组 team 调试标志:team 创建事件、线程事件、image 事件、停止新线程、以及系统调用前追踪
- 系统调用参数缓冲区在 64 位平台上被扩大过——此前在追踪时有内核内存破坏风险;调试器侧相应按目标架构区分
注意:这个调试接口与 BeOS"概念相似,但源码与二进制都不兼容"——拿 BeOS 时代的调试器接口假设去套会直接落空。
五、BeOS 兼容边界(别把它当"就是 BeOS")
- 目标是对 BeOS R5(x86)大范围二进制与源码兼容,但反过来不成立;某些情况下刻意破坏源码兼容以保住二进制兼容
- 已知差异举例:头文件路径从
be变为os;部分头文件名不同;类的私有字段改名;一些未文档化的 API 未实现;私有的设备映射 API 被磁盘设备 API 取代 - 调试接口不兼容;文件系统 API 也变过(BeOS 时代的文件系统驱动不能用)
- 对更早版本的兼容支持已被放弃
- 内核内部的改动也可能显式破坏兼容——例如某次为做锁竞争测量把自旋锁改成结构体,就明确标注会破坏二进制兼容
六、用户态服务边界
不是所有"Haiku 组件"都在内核里:图形服务器、应用注册服务一类是用户态服务,它们有自己的协议与生命周期。逆这类目标时:
不要找内核驱动接口
要看用户态服务的通信端点与协议
决策树
拿到 Haiku 目标
├─ 先判:驱动 / 内核子系统 / 用户态服务
│ ├─ 驱动 → device_node 树 + 两层模块
│ ├─ 内核子系统 → team/thread/area/port/semaphore + KDL
│ └─ 用户态服务 → 服务端点与协议(不在内核)
├─ 驱动"不存在"
│ ├─ 按需发现:只有被请求时才会搜索/加载
│ └─ 查总线类型规则与按需标志
├─ 在 driver module 里找不到 read/write
│ └─ 那是 device module(device_v1)的接口
├─ 内存/同步/消息相关异常
│ └─ 落到 area / semaphore(注意其扩展标志位)/ port
├─ 现场调试
│ ├─ KDL 可查栈回溯、线程、内存、系统日志、切换 CPU
│ └─ 调试接口与 BeOS 不兼容,别套旧接口
└─ 兼容性相关判断
├─ R5 二进制/源码兼容是单向的
└─ 调试接口与文件系统 API 属于不兼容的部分
工具与验证
- 驱动:device_node 树(module/attributes/resources)、两层模块的接口集合与命名约束、资源认领唯一性
- 系统:team/thread/area/port/semaphore 的归属与关系;信号量标志位
- 调试:KDL 命令与调试 API 消息结构(头部字段、team 调试标志、syscall 追踪)
- 兼容:目标是否来自 BeOS 时代(调试接口/文件系统 API 会直接落空)
- 验证:能同时说清「这个目标是驱动还是系统组件 + 它在哪一层、由谁加载 + 它用的对象属于哪套模型」
该平台的坑(汇总)
- 把按需发现当驱动缺失:初始只探索最小集合
- 在 driver module 里找
read/write:那是 device module 的接口 - 忽略
driver_v1/device_v1命名约束 - 期待
suspend/resume被调用:当前实现不调用 - 混用固定子节点与动态子节点
- 逆非驱动目标时仍钻进 Device Manager:入口应是内核对象模型或用户态服务
- 忽略信号量的扩展标志:可中断、释放全部、仅在等待时递减会改变行为
- 拿 BeOS 的调试接口或文件系统 API 假设去套:两者都不兼容
- 以为兼容是双向的:只保证"旧程序能跑",不保证新接口回退
- 忽略 64 位下调试 API 缓冲区结构的变化:按旧布局解析会错