第十五周工作总结
第一部分:实现延迟上下文切换机制
本周实现了JH7110平台的延迟上下文切换机制,解决了在中断处理过程中进行上下文切换可能导致的状态不一致问题。
工作目的
因为ecall是同步异常,在中断调用中执行会直接跳转到ecall执行流
在中断处理过程中调用 trigger_context_switch() 直接执行 ecall 会导致:
- 中断栈状态混乱:中断处理时的栈状态与 ecall 路径不一致
- 寄存器状态错误:中断保存的寄存器与上下文切换期望的状态不匹配
需要设计一个机制,在中断处理过程中延迟上下文切换,等到中断处理完成后再执行。
工作成果
设计思路
核心思想:在中断处理过程中设置标志位,中断退出时检查标志位决定是否进行上下文切换。
执行流程
中断发生
↓
进入 handle_exception()
↓
设置 IN_INTERRUPT = true
↓
处理中断
↓
清除 IN_INTERRUPT = false
↓
检查 NEED_CONTEXT_SWITCH
├─ false → 返回 0 → 正常 mret
└─ true → 返回 1 → 跳转到上下文切换实现
1. 全局标志变量
在 interrupt.rs 中添加:
rust
/// 全局标志:是否在中断处理中
pub(crate) static IN_INTERRUPT: AtomicBool = AtomicBool::new(false);
/// 全局标志:是否需要延迟的上下文切换
pub(crate) static NEED_CONTEXT_SWITCH: AtomicBool = AtomicBool::new(false);2. 中断处理逻辑修改
handle_exception() 返回值改为 usize:
- 返回 1:需要延迟的上下文切换
- 返回 0:正常返回
rust
pub unsafe extern "C" fn handle_exception(mcause: usize) -> usize {
if is_interrupt {
IN_INTERRUPT.store(true, Ordering::SeqCst);
handle_interrupt(exception_code);
IN_INTERRUPT.store(false, Ordering::SeqCst);
if NEED_CONTEXT_SWITCH.load(Ordering::SeqCst) {
NEED_CONTEXT_SWITCH.store(false, Ordering::SeqCst);
return 1; // 需要上下文切换
}
return 0; // 正常返回
}
...
}3. 触发上下文切换逻辑
trigger_context_switch() 检查中断状态:
rust
fn trigger_context_switch() {
if IN_INTERRUPT.load(Ordering::SeqCst) {
// 在中断中,设置延迟标志
NEED_CONTEXT_SWITCH.store(true, Ordering::SeqCst);
} else {
// 不在中断中,直接执行 ecall
unsafe { asm!("ecall"); }
}
}4. 汇编路径修改
在 trap.rs 的 __trap_entry 中添加新的执行路径:
assembly
// 调用 handle_exception(mcause)
"mv a0, t0",
"jal handle_exception",
// 检查返回值
"li t1, 1",
"beq a0, t1, 2f", // 返回1则跳转到标签2
// 标签0:正常返回路径(返回0)
...恢复寄存器...
"addi sp, sp, 144",
"mret",
// 标签1:直接 ecall 路径
...恢复 t0/t1/t2...
"addi sp, sp, 24",
"j MachineEnvCall",
// 标签2:延迟上下文切换路径(返回1)
...恢复所有寄存器...
...恢复 mepc...
"addi sp, sp, 144",
"j MachineEnvCall"三条执行路径对比
| 路径 | 触发条件 | 栈操作 | 最终操作 |
|---|---|---|---|
| 标签0 | 中断返回0 | 释放144字节 | mret |
| 标签1 | 直接ecall | 释放24字节 | MachineEnvCall |
| 标签2 | 中断返回1 | 释放144字节 | MachineEnvCall |
第二部分:sbi与StarryOS兼容性
工作过程
一、SBI启动方案尝试
1.1 RustSBI启动StarryOS(失败)
尝试环境:
- 测试平台:QEMU virt机器
- RustSBI版本:0.4.0
- 启动命令:
bash
qemu-system-riscv64 -m 1G -smp 1 -machine virt \
-bios rustsbi-prototyper-dynamic.bin \
-kernel StarryOS_riscv64-qemu-virt.bin \
-device virtio-blk-pci,drive=disk0 \
-drive id=disk0,if=none,format=raw,file=disk.img \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::6666-:6666,hostfwd=udp::6666-:6666 \
-nographic启动过程:
- ✅ RustSBI初始化成功
- ✅ 设备树配置完成(PMP、IPI、Console等)
- ✅ StarryOS内核加载并开始初始化
- ✅ 内存管理、任务调度、设备驱动初始化成功
- ✅ 文件系统和网络子系统启动
- ❌ 加载用户程序时发生panic
失败信息:
panicked at axcpu-0.3.0-preview.8/src/riscv/trap.rs:55:17:
Unhandled trap Exception(IllegalInstruction) @ 0xffffffc08037499c, stval=0xf2000053错误分析:
- 异常类型:非法指令异常(IllegalInstruction)
- 异常地址:
0xffffffc08037499c(内核代码段) - 异常值(stval):
0xf2000053
发现:
- 在真实硬件(VisionFive2)和QEMU中使用RustSBI都会失败
- panic原因不同,说明StarryOS可能依赖OpenSBI的某些特定行为
决策:暂时使用OpenSBI进行后续开发
二、系统调用与多核通信实现(尝试阶段,代码还没有提交)
2.1 跨核软件中断实现
目标:在StarryOS中实现系统调用,让用户态程序能够通过StarryOS → OpenSBI → 其他核心的路径触发软件中断
实现架构:
用户态程序
↓ syscall
StarryOS内核
↓ SBI调用
OpenSBI
↓ 软件中断
目标核心(运行Embassy Preempt)关键代码实现:
1. StarryOS系统调用接口
rust
// 在StarryOS中添加系统调用
const SYS_IPI_SEND: usize = 123;
fn sys_ipi_send(target_hart: usize, event: usize) -> isize {
// 调用OpenSBI的IPI扩展
sbi::send_ipi(target_hart, event);
0
}
// 系统调用分发
fn syscall_handler(id: usize, args: [usize; 3]) -> isize {
match id {
SYS_IPI_SEND => sys_ipi_send(args[0], args[1]),
// ... 其他系统调用
_ => -1,
}
}3. Embassy Preempt端接收中断
rust
// 在embassy_preempt的中断处理中
pub unsafe extern "C" fn handle_exception(mcause: usize) -> usize {
if is_ipi_interrupt(mcause) {
// 处理来自StarryOS的中断
handle_ipi();
IN_INTERRUPT.store(false, Ordering::SeqCst);
return 0;
}
// ...
}测试结果:✅ 成功实现跨核通信
三、后续计划与设计方案
3.1 多系统方案在StarryOS中的抽象
设计目标:将多系统通信方案抽象为字符设备
架构设计:
StarryOS用户态
↓ open/read/write/ioctl
/dev/multisys (字符设备)
↓ VFS层
多系统通信驱动
↓ SBI调用
OpenSBI
↓ IPI
Embassy Preempt