🔀 渐进式驱动迁移

在多核系统上,用 Linux + 裸机/RTOS 双核协作,逐函数迁移驱动——每一步都能验证正确性

Linux Kernel Module IPI 通信 逐函数验证 双核协作 🤖 自动化流水线 驱动迁移
🧪 实际进展(QEMU 实测验证): ✅ Step 1 virtblk_result — 7/7 自测 + 28 次真实 I/O 验证 100% 通过
✅ Step 2 virtblk_vbr_status — 9/9 自测 + 36 次真实 I/O 验证 100% 通过
✅ 公平性约束(C1-C5)— 1728 个随机样本全过,顺序分布 748:788 ≈ 1:1
✅ 路径覆盖率 — virtblk_result 7/7 (100%) · virtblk_vbr_status 3/3 (100%)
✅ Step 3 virtblk_cleanup_cmd — Claude Code 翻译 + 验证 1536/1536,100% 覆盖
🔧 Step 4+ — 转向自动化流水线:框架 + 主循环 + 统计(整驱动自动翻译、自动验证、自动提交,最后统一统计结果,不逐个手动介入)
📦 源码: ~/Code/dual-core-migration/ · 📜 翻译记录: docs/translation-log/

1核心问题:驱动翻译为什么难?

把 Linux 驱动翻译到另一个系统(裸机/RTOS)是一个高风险、低可验证性的任务。

❌ 传统方式:整体翻译 Linux 驱动(完整) 翻译后的驱动 问题: 1. 一次翻译几千行,无法逐段验证 2. 翻译错误只能在最后整体测试时发现 3. 调试成本极高——不知道错在哪 ✅ 新方式:渐进式迁移 func_A ✅已迁移 func_B ✅已迁移 func_C 🔄迁移中 优势: 1. 一次只迁移一个函数,立即验证 2. Linux 始终运行原始代码作为参照 3. 错误定位精确到单个函数

2整体架构:双核协作

一个核心跑 Linux(有源码),一个核心跑目标系统(裸机/RTOS),通过 IPI 通信。

Core 0: Linux 用户空间 应用程序 / 测试工具 / 设备节点 原始驱动代码 (正在逐步删除) funcA: ❌ 已删除 Stub 函数 已迁移的函数 → 转发到 Core 1 funcA() { ipi_call(); } 🔧 Kernel Module(迁移控制器) 挂载/卸载驱动 · 管理函数映射表 · IPI 通信 · 验证对比 insmod → 注册 stub · rmmod → 恢复原始函数 📬 IPI 通信层 共享内存 + IPI 中断 · 函数ID + 参数序列化 + 返回值 🔗 共享内存区域 函数调用请求 · 参数缓冲 · 返回值缓冲 · 状态标志 硬件(通过 MMIO 访问外设) Core 1: 裸机 / RTOS 已迁移的驱动函数 funcA() ✅ · funcB() ✅ · funcC() 🔄迁移中 每个函数独立实现、独立测试、独立验证 驱动实现 目标系统的 API 可能用 Rust 重写 可形式化验证 验证框架 对比 Linux 输出 寄存器读写对比 行为一致性检查 📬 IPI 中断处理 接收函数调用请求 · 反序列化参数 · 调用本地实现 · 返回结果 🔗 共享内存区域(对端) 读取请求 · 写入返回值 · 状态同步 硬件访问(独占或共享) 迁移后的函数直接操作硬件 · 通过 MMIO 或设备接口 函数调用请求 返回值 共享内存

3逐函数迁移流程

单向迭代推进:翻译单元1 → 验证 → ✅ 通过 → 单元2 → 验证 → ... 直到所有单元都正确翻译。验证不通过就停在该单元,不允许带病前进。Linux 始终作为参照系。

Phase 1: 选择函数 Phase 2: 实现 & 注入 Phase 3: 验证 Phase 4: 确认 ① 分析驱动 找到要迁移的函数 分析函数依赖 确定参数和返回值 选择原则: • 叶子函数优先 • 依赖少的优先 ② 实现 & 注入 在 Core 1 上实现函数 注册到函数映射表 Kernel Module 加载 stub 关键操作: • insmod 注册 stub 函数 • 替换函数指针 ③ 对比验证 同一输入分别调用: Linux 原始函数 vs Core 1 对比输出/寄存器/行为 验证维度: • 返回值一致性 • 硬件状态一致性 ④ 确认迁移 从 Linux 删除 该函数的原始代码 进入下一个单元 → 单向推进,直到全部完成 具体数据流:一次函数调用的过程 用户调用 funcA() Stub 拦截 序列化参数 写共享内存 funcID + args 发送 IPI 中断 Core 1 Core 1 执行 读参数 → 调用 → 写结果 返回值到 Core 0 用户 验证模式:同时调用两个版本,对比结果 Linux 原始路径 funcA(args) → result_L 寄存器状态: reg_L 硬件状态: hw_L ?= Core 1 迁移路径 funcA(args) → result_N 寄存器状态: reg_N 硬件状态: hw_N ✅ result_L == result_N ✅ reg_L == reg_N → 迁移成功,可以删除 Linux 版本

4Kernel Module:迁移控制器

核心组件——管理函数映射、IPI 通信、验证对比。用 kernel module 实现,支持热插拔。

📥 insmod 时

1. 注册要迁移的函数列表
2. 为每个函数创建 stub
3. 替换函数指针(或注册为模块接口)
4. 初始化共享内存和 IPI 通道

⚡ 运行时

1. 拦截函数调用
2. 序列化参数到共享内存
3. 发送 IPI 到 Core 1
4. 等待返回值(轮询或中断)
5. 反序列化结果返回调用者

📤 rmmod 时

1. 恢复原始函数指针
2. 清理共享内存
3. 通知 Core 1 释放资源
4. 输出迁移验证报告

🔍 验证模式

1. 同时调用原始函数和迁移函数
2. 对比返回值
3. 对比寄存器/内存状态
4. 记录差异,生成报告

// kernel module: migration_controller.c #include <linux/module.h> #include <linux/smp.h> // 函数映射表 struct func_map { const char *name; void *original; // Linux 原始函数 void *stub; // IPI 转发 stub int func_id; // Core 1 的函数 ID bool migrated; // 是否已迁移 }; // IPI 调用 stub(替换了原始函数) static long stub_gpio_set_value(unsigned int gpio, int value) { // 1. 写参数到共享内存 shared_mem->func_id = FUNC_GPIO_SET_VALUE; shared_mem->args[0] = gpio; shared_mem->args[1] = value; shared_mem->status = STATUS_READY; // 2. 发送 IPI 到 Core 1 smp_call_function_single(1, ipi_handler, NULL, 0); // 3. 等待返回值 while (shared_mem->status != STATUS_DONE) cpu_relax(); return shared_mem->retval; } // 加载时注入 static int __init migration_init(void) { // 保存原始函数指针 original_gpio_set_value = gpio_set_value; // 替换为 stub gpio_set_value = stub_gpio_set_value; // 通知 Core 1 准备好 init_shared_memory(); return 0; } // 卸载时恢复 static void __exit migration_exit(void) { gpio_set_value = original_gpio_set_value; // 恢复 cleanup_shared_memory(); }
函数名状态Core 1 实现验证结果操作
gpio_set_value✅ 已迁移gpio_set_value_impl100% 一致已删除 Linux 版
gpio_get_value✅ 已迁移gpio_get_value_impl100% 一致已删除 Linux 版
gpio_direction_output🔄 验证中gpio_dir_out_impl98% 一致调查差异中
virtblk_result✅ 已迁移core1_virtblk_result100% 一致 (28 次验证)Step 1 完成
virtblk_vbr_status✅ 已迁移core1_virtblk_vbr_status100% 一致 (36 次验证)Step 2 完成
virtblk_cleanup_cmd🔄 迁移中待验证下一步 (Step 3)
gpio_chip_register⏳ 待迁移依赖较多

5.5公平性约束(Fairness Constraints)

对比验证必须满足 5 条约束,防止"碰巧对固定用例正确"的假阳性——证明翻译实现在整个输入空间上行为一致。

约束含义实现实测
C1 输入公平两边收到逐字节相同的输入同源拷贝,禁止分别构造
C2 顺序公平不固定"先 Linux 后 Core1"随机化执行顺序✅ 748:788 ≈ 1:1
C3 随机覆盖不只用人工固定用例全空间随机输入 + 边界值✅ 0-255 全空间
C4 重复一致同输入多次执行结果一致重复 3 次校验✅ 违反 0 次
C5 统计对称两边执行次数严格相等计数器校验✅ 违反 0 次
为什么需要公平性约束?固定用例验证只能证明"翻译实现碰巧对这 N 个输入正确"; 公平性验证用全空间随机输入 + 随机顺序 + 重复一致 + 统计对称证明 翻译实现在整个输入空间上行为一致。

QEMU 实测结果: virtblk_result 1536 样本(0-255 全空间 × 6 轮 I/O)100% 通过; virtblk_vbr_status 192 随机布局样本 100% 通过; IPI 平均延迟 ~1.1μs。

5.6自动化翻译流水线:框架 + 主循环 + 统计

不硬编码、不逐个介入——框架和循环写好之后,整个驱动自动完成翻译,最后统一统计结果。人类只出现在两个点:① 写框架(一次性) · ② 看最终统计报告、处理失败清单

🎯 核心原则:循环全自动——提取函数、LLM 翻译、注入 stub、IPI 验证、提交/回滚都不需要人介入。框架不针对任何具体函数硬编码,而是从源码动态读取函数列表、按依赖排序、动态生成翻译 prompt。最终呈现给人的只有统计结果失败函数清单

🛠 框架组件(写一次,跑整个驱动)

🔍 函数提取器

解析驱动源码 → 全部函数 + 调用图
拓扑排序:叶子函数优先、依赖少的先迁移
提取每函数签名 / 依赖 / 操作的寄存器

🤖 翻译引擎

按签名 + 上下文动态生成 prompt(无硬编码)
LLM 输出目标系统实现 + 测例表
失败自动换模型 / 重试(可配置次数)

🔧 注入器

自动生成该函数的 stub 代码
更新函数映射表(func_id → 实现地址)
kernel module 挂载 stub、保留原函数指针

✅ 验证器

IPI 对比 Linux 原始实现
C1–C5 公平性约束 + 覆盖率插桩
全空间随机输入 + 边界值,自动判定

📦 提交器

通过 → git commit(每函数一个提交点)
删除 Linux 原函数代码
失败 → 自动回滚 + 记录失败原因

📊 统计器

队列耗尽后生成最终统计报告
成功/失败/通过率/覆盖率/耗时
失败原因分类 + 剩余函数清单

① 提取函数 + 依赖排序 源码 → 函数列表 + 调用图(叶子优先) ② LLM 翻译(动态 prompt) 无硬编码 · 失败自动重试 ③ 注入 stub + 挂载 生成 stub · 更新映射表 · insmod ④ IPI 对比验证 C1–C5 公平性 + 覆盖率插桩 通过? ⑤ ✅ 提交 + 删除原函数 git commit · 从 Linux 移除 下一个函数(队列自动推进) ⑥ ❌ 回滚 + 记录原因 恢复原函数 · 失败分类 重试 / 记入失败清单 队列空 → 结束循环 📊 最终统计报告(人只看这个)
# 主循环:写完一次,整驱动自动跑完(无人工介入) funcs = extract_functions(driver_src) # 提取全部函数 + 调用图 order = topo_sort(funcs, deps="leaves-first") # 依赖排序 failures = [] for f in order: # 逐个自动翻译 impl = llm_translate(f) # LLM 翻译(动态 prompt,无硬编码) stub = gen_stub_and_inject(f) # 生成 stub + kernel module 挂载 report = verify(f, impl, stub) # IPI 对比 + C1-C5 + 覆盖率 if report.passed: commit_and_remove_original(f) # ✅ 提交 + 删除 Linux 原函数 else: rollback(f) # ❌ 回滚 + 记录失败原因 failures.append(f) stats = summarize(order, failures, reports) # 最终统计(人只看这个)

📊 最终统计报告(自动生成,示例)

📋 驱动迁移统计 · driver_name · 2026-08-01(示例) 198 ✅ 成功迁移(92.5%) 198 / 214 个函数 16 ❌ 失败(7.5%) 进入人工分析清单 98.7% 📊 平均路径覆盖率 F2 分支级 · 全函数 3h 42m ⏱ 总耗时 · IPI 平均 ~1.1μs 平均 63s / 函数(含验证) 迁移进度 198 / 214 · 92.5% 失败原因分类(自动归类) LLM 幻觉 7 依赖缺失 5 硬件时序 3 其他 1
维度手动逐个翻译(Step 1–3)自动化流水线(Step 4+)
翻译方式人逐个写 prompt / 硬编码框架 + 循环自动翻译
人工介入每个函数都要介入只有 ①写框架 ②看统计
进度反馈手动维护记录循环自动推进 + 日志
最终产出人工汇总自动统计报告 + 失败清单
定位原型验证(已完成)规模化翻译整个驱动
Step 1–3 的角色:手动阶段不是白做的——它验证了「IPI 对比 + 公平性约束 + 覆盖率」这套机制可行;现在把机制封装成框架 + 主循环,把「人逐个翻译」替换成「循环自动翻译」,就能规模化跑完整个驱动,最后只交付一份统计报告。

5.7翻译路径覆盖率(Coverage)

LLM 翻译时不断构建测例覆盖翻译路径,翻译后输出覆盖率报告——证明"测过所有路"。

📊 三级覆盖率

F1 函数级 — 函数是否被执行
F2 分支级 — 每个 if/switch 分支是否命中
F3 路径级 — 每个 case→return 关键路径

实现:翻译代码关键分支点插入 COV_TRACE 插桩

🧪 LLM 必须产出测例表

prompt 内置要求:
• 每个 case/if 分支至少一个用例
• 边界值(0, 0xFF)+ 异常值(default)
• 每用例标注:输入 → 预期输出 → 覆盖分支

✅ QEMU 实测(step_coverage.ko)

virtblk_result: 执行 1536 次,分支 7/7 (100%)
virtblk_vbr_status: 执行 384 次,分支 3/3 (100%)

结论:✅ 100% 路径覆盖

fairness × coverage = 翻译可信: 公平性约束保证结果一致(正确性),路径覆盖率保证测过所有路(完备性)。 两者结合,才能说一个函数"安全迁移"。

5对比现有方案

为什么这种方案比传统翻译更好?

维度 整体翻译 reharness (自动抽取) 渐进式迁移(本方案)
验证粒度 只能整体测试 语义级别 逐函数验证 ✅
正确性保证 翻译完才知道 模式匹配 每步对比原始行为
调试难度 极高(不知错在哪) 中等 低(精确定位到函数)
迁移风险 全有或全无 中等 渐进,随时可回滚
需要停机 迁移期间停机 不需要 函数级切换,影响小
自动化程度 手动翻译 自动抽取 全自动(框架 + 循环 + 统计)
适用范围 任意驱动 MMIO 驱动 任意驱动
💡 与 reharness 的互补关系:reharness 可以自动抽取驱动的设备规格(寄存器语义、角色推断),这些信息可以帮助指导渐进式迁移——知道哪些函数先迁移、每个函数操作哪些寄存器、预期行为是什么。两者结合:reharness 做前期分析,渐进式迁移做后期执行和验证。

6关键技术细节

IPI 通信、共享内存、硬件访问权——三个需要解决的核心问题。

📬 IPI 通信

IPI(Inter-Processor Interrupt)是多核处理器核间中断机制。

流程:Core 0 写共享内存 → 触发 IPI → Core 1 中断处理 → 读参数 → 执行 → 写结果 → 触发 IPI 回 → Core 0 读结果

延迟:IPI 本身约 1-10μs,函数执行时间主导总延迟。

替代方案:也可以用 mailbox、RPC over shared memory 等。

🔗 共享内存

两个核心需要共享一块内存区域用于参数和返回值传递。

设计:
• 固定大小的环形缓冲区
• 结构体:func_id + args[N] + retval + status
• 需要处理 cache 一致性(flush/invalidate)
• 使用内存屏障保证顺序

注意:裸机核心需要配置 MPU/MMU 允许共享区域访问。

🔌 硬件访问权

迁移后的函数需要访问硬件(MMIO),但硬件可能被 Linux 驱动占用。

方案:
独占模式:Linux unmap MMIO 区域,Core 1 独占(干净但风险高)
代理模式:所有 MMIO 访问通过 IPI 代理(安全但慢)
分时模式:迁移函数的 MMIO 由 Core 1 访问,其余由 Linux 管理

推荐:先用代理模式验证,确认正确后再切独占模式。

🔄 回滚机制

如果迁移的函数验证失败,需要能快速回滚。

设计:
• Kernel Module 保存所有原始函数指针
rmmod 自动恢复所有函数
• 支持单个函数回滚(不影响已迁移的)
• 验证失败自动触发回滚

关键:回滚是 O(1) 操作——只需恢复函数指针。

7与 AgentOS 的关系

渐进式迁移是 AgentOS 从 Linux 生态中"长出来"的关键路径。

Linux 完整驱动 完整生态 (起点) 渐进迁移 双核混合 Core 0: Linux(逐渐精简) Core 1: AgentOS(逐渐增长) 每一步都可验证 (中间态) 全部迁移 AgentOS 自主驱动 NL 驱动行为 (终点) reharness 辅助:自动分析驱动语义 💡 渐进式迁移让 AgentOS 不需要"从零写驱动",而是从 Linux 生态中逐步"长出来"
核心价值:AgentOS 不需要重新实现所有 Linux 驱动。通过渐进式迁移,可以从 Linux 出发,逐函数验证,逐步过渡。最终目标是所有驱动函数都在 Core 1 上运行,Linux 核心可以完全移除。整个过程是可逆的——任何时候发现问题,都可以回滚到 Linux 版本。

渐进式驱动迁移 · Dual-Core Incremental Driver Migration

2026-08-01 · AgentOS 系列 · 内核 Agent → · AgentOS 总览 →