cove-salus-tellus测试程序时序逻辑

一、核心说明

以下时序图基于

https://github.com/rivosinc/salus/blob/main/test-workloads/src/bin/tellus.rs

提供的Tellus测试程序逻辑,聚焦主核心(hart 0) 的核心执行流程,覆盖TVM创建、运行、中断/异常处理、资源回收全生命周期;次级核心(hart 1+)仅简化展示"启动→自旋等待任务"逻辑,不展开细节。

时序图使用Mermaid语法,可直接复制到支持Mermaid的工具(如Mermaid Live Editor、VS Code Mermaid插件)中渲染查看。

二、完整时序图

主核心 次级核心 TVM 阶段1:初始化(基础环境准备) kernel_init入口,初始化控制台/FDT/IMSIC 调用hart_start启动次级核心 secondary_init,自旋等待IPI任务 验证COVE扩展,完成初始化 阶段2:TVM创建(资源配置与最终化) 捐赠页→创建TVM→注册共享内存 添加vCPU0/AIA配置/Guest数据页 TVM_finalize,设置入口+中断预配置 阶段3:TVM运行&异常处理(核心循环) 调用tvm_run运行Guest 触发Trap(ECALL/页故障/中断) 处理Trap,关闭对应中断/映射缺页 循环调用tvm_run继续运行 阶段4:资源回收(校验与系统关机) 向量校验→AIA解绑→销毁TVM 验证共享页→回收所有捐赠页 PMU测试→注销共享内存→系统关机 主核心 次级核心 TVM Tellus测试程序核心时序(RISC-V S-mode TVM测试)

start.S
主核心 SBI 次级核心 主核心(hart0)启动流程 触发_start汇编入口 初始化GP全局指针 清空sstatus/sie,禁用所有中断 循环清零BSS段(未初始化全局变量) 设置SP栈指针指向_stack_end 调用Rust层kernel_init(传入hart_id/fdt_addr) kernel_init完成初始化(控制台/FDT/IMSIC) 理论分支:kernel_init返回后进入WFI循环 次级核心(hart1及以上)启动流程 触发_secondary_start汇编入口 初始化GP全局指针 清空sstatus/sie,禁用所有中断 SP/TP绑定a1(PerCpu结构体地址) 调用Rust层secondary_init secondary_init完成,进入WFI休眠循环 (可选)通过IPI唤醒次级核心执行任务 主核心 SBI 次级核心 Tellus程序核心启动完整时序(RISC-V S-mode)

三、关键节点补充说明

  1. 时序核心逻辑

    • 初始化阶段:完成硬件配置(IMSIC、CPU、内存)和环境准备;
    • TVM创建阶段:核心是"捐赠页→创建TVM→配置资源(内存/中断)→最终化",所有convert_pages后需调用fence_memory保证内存一致性;
    • 运行循环:核心是"tvm_run→捕获Trap→处理Trap→循环",覆盖Guest ECALL、缺页、中断三大核心场景;
    • 回收阶段:先校验数据(向量寄存器、共享页),再销毁TVM、回收所有捐赠页,最后执行PMU测试并关机。
  2. 次级核心简化逻辑

    次级核心启动后仅执行secondary_init→进入TaskRunner::spin,等待主核心通过IPI发送任务(如fence_memory中的tsm_local_fence),任务执行完成后回到WFI休眠,时序图中未展开此细节(仅标注"自旋等待任务")。

  3. 中断/异常处理关键

    • 预配置的stimecmp=0si_eip[0] pending位会触发首次中断,处理时会关闭对应中断使能;
    • Guest侧的外部中断通过SupervisorGuestExternal投递,主核心侧外部中断通过SupervisorExternal投递,两者严格区分。

四、启动逻辑总结

该时序图核心覆盖:

  1. 初始化→TVM创建→运行循环→资源回收的全生命周期;
  2. 关键API调用(cove_host/中断/AIA相关)的执行顺序;
  3. Trap处理的分支逻辑(ECALL/缺页/中断)。

五、次级核心启动

rust 复制代码
/// Starts the given cpu executing at `start_addr` with `opaque` in register a1.
///
/// # Safety
///
/// start_addr must point to code that can be safely executed.
/// opaque, if a pointer, must point to data that is safe to access from the newly running context.
pub unsafe fn hart_start(hart_id: u64, start_addr: u64, opaque: u64) -> Result<()> {
    // 1. 构造SBI消息结构体:封装hart_id/start_addr/opaque
    let msg = SbiMessage::HartState(HartStart {
        hart_id,       // 要唤醒的次级核心ID(如1/2/3)
        start_addr,    // 次级核心启动后跳转的地址(_secondary_start的物理地址)
        opaque,        // 传给次级核心的a1参数(PerCpu结构体地址)
    });
    // 2. 发送SBI消息并执行ecall调用
    // Safety注释:保证start_addr是合法的次级核心启动代码地址
    ecall_send::<()>(&msg)?;
    Ok(())
}

这段代码的核心是SbiMessageecall_send,它们是对底层ecall指令的封装,内部逻辑等价于之前的汇编内联代码:

rust 复制代码
// (底层ecall_send的简化实现,帮你补全上下文)
fn ecall_send<T>(msg: &SbiMessage) -> Result<T> {
    unsafe {
        // 按SBI约定,将msg参数映射到a0-a7寄存器
        let (a0, a1, a2, a6, a7) = msg.into_registers();
        let ret: SbiRet;
        // 执行ecall指令,触发SBI调用
        asm!(
            "ecall",
            in("a0") a0,
            in("a1") a1,
            in("a2") a2,
            in("a6") a6,
            in("a7") a7,
            lateout("a0") ret.error,
            lateout("a1") ret.value,
            options(nostack),
        );
        // 处理返回值,转换为Rust的Result类型
        if ret.error == 0 {
            Ok(unsafe { core::mem::transmute(ret.value) })
        } else {
            Err(SbiError::from(ret.error))
        }
    }
}
  • SbiMessage::HartState:对应SBI的HSM扩展(0x48534D),HartStart对应扩展内的hart_start函数(函数号0);
  • into_registers():将hart_id/start_addr/opaque分别映射到a0/a1/a2a6=函数号,a7=扩展号,和手动调用的寄存器约定完全一致。
相关推荐
这儿有一堆花1 小时前
告别“脚本小子”:真正理解 Linux 包管理器
linux
噜啦噜啦嘞好1 小时前
Linux:线程池
linux·运维·c++
代码不行的搬运工1 小时前
使用多代理间 AS 诊断系统检测和恢复前缀劫持(2010)
网络·bgp安全
屿行屿行1 小时前
【Linux】音视频处理(gstreamer和ffmpeg的实际应用)
linux·ffmpeg·音视频·视频编解码
用户31187945592181 小时前
银河麒麟V10 申威架构 docker-compose rpm 包安装教程(附命令)
linux
峥嵘life1 小时前
Android EDLA 搭建Linux测试环境简介
android·linux·运维
小嘟嘟131 小时前
第3章 Shell 条件判断:解决 90% 的分支逻辑问题
linux·运维·shell
怕什么真理无穷1 小时前
解析OSI 七层模型
网络
⁤⁢初遇1 小时前
Linux------线程概念与控制
linux·运维·服务器