3.2 CSR 体系与陷入处理:系统的神经末梢


3.2 CSR 体系与陷入处理:系统的神经末梢

本节摘要:控制状态寄存器 CSR 是特权架构的状态仓库,陷入与中断处理则是它的最大用户。本节先摸清 CSR 地址空间的编址与访问指令,再按"进入自动、退出手动、路由看委托"的框架完整走一遍异常进出路径,最后以扩展一个真实中断处理程序收尾。

地址即语义的寄存器仓库

CSR 的编址方式本身就是文档:十二位地址空间共四千零九十六个槽位,最高两位标明归属——00 开头归用户级,01 开头归监督级,11 开头归机器级,10 段留给虚拟化与自定义。读地址就知道这个寄存器是给谁用的、最低要什么特权级才能碰。访问它们的指令只有一小簇:csrrw 写入并取回旧值,csrrs 按位设置,csrrc 按位清除,另有各自的立即数变体。这簇指令都是原子语义——csrrw 一条指令完成新旧交换,读改写竞争在硬件层就不存在,这正是中断开关能靠它安全操作的原因。

高频 CSR 值得列队认识。控制流三件套:mtvec 存陷入入口地址,mepc 存返回地址,mcause 存事件原因。状态中枢:mstatus 里挤满了全局开关,MIE 控制机器级中断总闸,MPIE 是陷入前的旧闸位,MPP 记录陷入前特权级。中断兵籍册:mie 是各路中断的独立使能,mip 是它们的待决标志——真正的中断是否送达,取决于总闸、分闸与待决位三者同时为真。配置面:medeleg 与 mideleg 决定事务下放,mtval 捎带出错信息,misa 报告实现支持的指令集字母。这一排名字先混个脸熟,下文走路径时会逐一入戏。

异常进入:硬件自动的五件事

异常或中断触发的一瞬,硬件按固定剧本自动完成五件事,随后才轮到软件。第一,返回地址写进 mepc——注意对异常而言是出错指令本身,对中断而言通常是下一条指令,这个差别决定处理完后要不要重执行。第二,事件编号写进 mcause:最高位是一是中断、零是异常,低位是编号,比如值二代表非法指令、值十一代表机器级外部中断。第三,出错的相关信息写进 mtval,页错误时是出错地址,非法指令时是指令编码。第四,旧特权级记入 mstatus 的 MPP,旧中断闸位挪进 MPIE、总闸 MIE 自动拉下——嵌套保护在这里生效,处理程序不会被同类中断再次打断,除非它自己开闸。第五,特权级升至机器级,pc 指向 mtvec。

mtvec 有两种模式,最低两位是开关。直接模式下所有陷入都跳向同一地址,处理程序自己读 mcause 分诊;向量模式下中断跳向入口加四乘事件编号——每路中断一个独立入口,省一次软件分诊,异常则仍挤公共入口。选哪种是延迟与灵活性的交易:向量模式快,但要为每个中断号预留四字节槽位。

委托:把路由权下放给操作系统

按上面的剧本,一切事务都涌向机器级——那操作系统岂不是形同虚设?委托寄存器就是为此设计的分流阀:medeleg 与 mideleg 各是三十二位的位图,置一的位表示对应编号的事务不劳机器级,直接按监督级剧本(stvec、sepc、scause 一套镜像寄存器)送交监督级处理。典型配置:系统调用(用户级 ecall,异常编号八)与时钟中断委托给监督级,让 Linux 的慢路径不经过固件;机器级只留下必须由自己处理的硬错误。

委托有一条兜底规则:被委托的事务若目标级压根没有处理能力(比如监督级未实现),硬件自动回退交机器级。设计意图是清晰的——委托是性能优化,不是可靠性依赖;固件永远保留最后一道闸。

上图是本节的地基图:进入时装哪套寄存器、退出时降回哪一级,全部由"事务编号加委托位图"这一对输入决定。把它记牢,任何陷入相关的疑难都能先在这里对号。

图 3-2 委托路由与寄存器装填图

图 3-2 委托路由与寄存器装填图

中断的三把闸与异常的分诊表

中断能不能打断当前 hart,要看三把闸同时为真:全局闸 mstatus 的 MIE、分闸 mie 里对应位、以及 mip 里的待决位。排错口诀随之而来:中断不来,按"待决、分闸、总闸"顺序逐级排查,三处读数一对照,断点基本锁定。异常没有闸,发生即触发——它们的处理路径由编号决定,常用编号值得背下:零与一是取指未对齐与访错,二是非法指令,三是断点,四到七是访存未对齐与访错,八、九、十一是三个特权级各自的 ecall 系统调用门铃,十二到十五是页错误家族。调试器打断点靠的就是三号断点异常,页错误家族则是下一章的常客。

工程现场:给裸机固件补一个定时器中断

背景:一块双 hart 测试板跑裸机固件,需要每毫秒一个心跳中断驱动调度轮询,定时器外设已就绪。

操作:初始化代码按依赖顺序铺开。

la t0, trap_entry ori t0, t0, 0 # 直接模式,地址四字节对齐 csrw mtvec, t0 # 挂上机器级陷入入口 li t1, 0x80 # 机器级定时器中断分闸位 csrw mie, t1 csrs mstatus, 0x8 # 拉起总闸 MIE # 定时器外设的周期配置在此略

trap_entry 里按序做四件事:把全部调用者保存寄存器压栈(中断可能在任意指令边界插入,必须保全现场);读 mcause 分诊;确认是定时器后重装下次到期值并置标志;恢复寄存器、执行 mret。

结果:第一次调试就翻车——心跳时有时无。读 mip 发现待决位置位了,但核心迟迟不进处理程序;再读 mstatus,总闸 MIE 是零。追根溯源,主程序某处调用的库函数里有 csrc mstatus 关中断却忘记恢复,心跳于是随机失踪。

解读:这次翻车是"三把闸"口诀的实战注脚:待决位只说明外设在敲门,门开不开取决于两级闸位;库代码擅自动总闸而不恢复,就是典型的门被反锁。排错路径也值得复述一遍——先读 mip 确认事件源,再查 mie 分闸,最后查 mstatus 总闸,十分钟锁定一个"时有时无"的玄学问题。

变式:把 mtvec 切到向量模式,为定时器中断单开一路入口,省掉软件分诊分支;再把定时器中断委托给监督级(需要先实现最小 S-mode 环境),对比两条路径的处理延迟差——这个数据会直接告诉你内核态中断要不要绕行固件。

本节要点回顾

  • 地址即语义:CSR 十二位编址自带特权级归属,访问指令一簇五式,原子交换是安全开关的根基;
  • 进入自动五件事:mepc、mcause、mtval、mstatus 状态装填、跳向 mtvec,硬件全程包办;
  • mtvec 双模式:直接模式省空间,向量模式省分诊,按延迟需求选;
  • 委托是分流阀:medeleg 与 mideleg 把事务下放监督级,兜底规则保证不因委托失效而丢事务;
  • 中断三把闸:待决位、分闸 mie、总闸 mstatus,三级依次排查是心跳失踪类故障的标准动作。

CSR 与陷入的地基打完,下一节把这些能力串成一条完整的启动链路:一块裸板从上电到跑起 C 程序要交接几棒,每一棒失败长什么样——那是一场排错实录。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U