前言
上一篇文章中介绍了loongarch架构中的地址翻译模式及其配置方法,涉及到虚拟内存系统中页表相关的管理。本文中则介绍TLB相关的异常处理,并结合代码进行分析。因为loongarch架构中采用的是一种软件管理TLB的方法,所以其处理流程和软件所需要进行的管理操作与很多常见的架构不同。
1、TLB表项和页表项
首先介绍TLB表项和页表项的格式,作基本的了解。
(1)页表项格式
下图为loongarch中的页表项格式:
下面为各位的说明:
- V:有效位。标志页表项是否有效。
- D:脏位。标志页内容是否需要回写。
- MAT:存储访问类型。见前面的文章。
- G:是否为全局页。
- P:物理页是否存在。
- W:是否可写。
- H:是否为大页。
- PA:物理地址高地址部分。
- NR:不可读位。表示该页不可读。
- NX:不可执行位。表示该页不可执行。
- PLV和RPLV:特权级,当RPLV=0,表示可以被任何特权级不低于PLV的程序访问;当RPLV=1,表示仅可以被特权级等于PLV的程序访问
其中,大页的页表项和基本页的页表项在格式上的主要区别是H位和G位。并且基本页的页表项在末级页表,而大页的页表项实际上是替代了原来的页目录项。
另外,对于基本页的页表项,loongarch中每个页表项存放了相邻的一对奇偶相邻页表信息。如下图:
(2)TLB表项格式
下图为loongarch中的TLB表项格式:
其中,每个TLB表项分为两个部分:第一行为比较部分,下面两行为物理转换部分。
TLB表项的比较部分包括:
- E:存在位,1比特。为1时表示存在。用于TLB查找时判断。
- ASID:address space identifier,10比特。用于区分不同的进程或虚拟地址空间,减少进程上下文切换等操作时刷新TLB的性能损失。TLB查找时也需匹配ASID。具体见后面的文章。
- G:全局位,1比特。用于取消ASID的作用,标识为全局的表项。
- PS:页大小,6比特。用于支持不同的页大小。如对于16KB的页,PS=14。
- VPPN:虚双页号,对应一对页表项信息。对于基本页的页表项,loongarch中每个页表项存放了相邻的一对奇偶相邻页表信息,所以TLB表项中存放的虚页号是系统中虚页号/2的内容,即虚页号的最低位不需要放在TLB中。查找TLB时根据被查找虚页号的最低位决定是选择奇数号页还是偶数号页的物理转换信息。
而对于大页的页表项,硬件上会自动将其拆分为两个尺寸折半的页表项存储在TLB中。如对于32MB的大页,TLB会存储两个16MB大小的表项。
因为TLB和页表双页存储的特性,每个TLB表项中有两个物理转换信息。物理转换信息中PPN即物理页号,其他和上文中页表中的对应。
2、软件管理TLB
类似于MIPS架构,loongarch中使用的是一种软件管理TLB的方式。在大多数其他的架构中,采用的都是通过硬件管理TLB的方式。软件管理TLB带来了更多的灵活性,但性能相对较差。
硬件管理TLB中,在忽略page fault的细节和cache的情况下,虚拟地址转换到物理地址的过程如下图:
其中,TLB miss后查找页表的这个过程是由硬件自动完成的,软件只需处理后面产生的page fault。整个过程中最多产生一次page fault。
下图则为软件管理TLB方案中虚拟地址转换到物理地址的过程:
具体解释如下:
- TLB miss后会产生第一次TLB重填(TLB refill)异常,在TLB重填异常中需要软件去遍历页表并填充TLB表项,这里应该是软件管理TLB和硬件管理TLB最大的不同。
- 然后TLB重填异常处理中,如果填充的页表项仍然无效,那么在返回后再次查询TLB时,会第二次产生其他的fault处理,以填充页表项、或填充页表项和TLB项。
- 如果第二次fault处理中没有重填TLB表项,那么在返回后再次查询TLB时,还会产生第三次TLB重填异常。
可以看到,TLB miss后查找页表的这个过程需要软件进行处理,并且整个过程中最多能产生三次异常。
另外,硬件上会保证在TLB重填异常中不能再次产生TLB重填异常。
3、TLB相关异常
loongarch中TLB相关的异常有:
- TLB重填异常:TLB miss后产生该异常
- load操作页无效异常:load操作的虚拟地址在TLB中找到了匹配项,但该匹配项的V=0时触发
- store操作页无效异常:store操作的虚拟地址在TLB中找到了匹配项,但该匹配项的V=0时触发
- 取指操作页无效异常:取指操作的虚拟地址在TLB中找到了匹配项,但该匹配项的V=0时触发
- 页特权等级不合规异常:访存操作的虚拟地址在TLB中找到了匹配项且V=1,但访问的特权等级不合规时触发。参考上文页表项中的PLV和RPLV域。
- 页修改异常:store操作的虚拟地址在TLB中找到了匹配项且V=1且特权合规,但该匹配项的D=0时触发
- 页不可读异常:load操作的虚拟地址在TLB中找到了匹配项且V=1且特权合规,但该匹配项的NR=1时触发
- 页不可执行异常:取指操作的虚拟地址在TLB中找到了匹配项且V=1且特权合规,但该匹配项的NX=1时触发
其中,TLB重填异常时需遍历页表进行重填工作。TLB重填异常于一般的异常不同,其拥有独立的异常入口、独立的用于维护现场的控制状态寄存器和一套独立的TLB访问接口控制寄存器,并且因此TLB重填异常可以在其他异常处理过程中被触发。而当进入TLB重填异常时,硬件会自动设置CSR.CRMD.DA=1和CSR.CRMD.PG=0,即进入直接地址翻译模式,从而避免在TLB重填异常中不能再次产生TLB重填异常。
而如load操作页无效异常等异常,则需要完成类似于page fault的工作。
4、相关指令
在介绍TLB相关异常的处理之前,先对loongarch中相关的指令进行介绍。
(1)TLB异常处理相关指令
- tlbsrch:查询TLB对应index。该指令使用CSR.ASID和CSR.TLBEHI中的信息查询TLB,如果命中则将其对应index写入CSR.TLBIDX.Index,否则将CSR.TLBIDX.NE置为1。index可用于tlbwr等指令,指示操作的TLB索引。
- tlbrd:读取index对应TLB表项。将CSR.TLBIDX.Index作为索引,读取TLB中的指定项。如果该TLB项有效,则将该TLB项的页表项信息写入到CSR.TLBEHI、CSR.TLBELO0、CSR.TLBELO1和CSR.TLBINX.PS这些相关寄存器,并将CSR.TLBIND.NE置0;如果无效,则将CSR.TLBIND.NE置1。
- tlbwr:写入index对应TLB表项。将CSR.TLBIDX.Index作为索引,把CSR.TLBEHI、CSR.TLBELO0、CSR.TLBELO1和CSR.TLBINX.PS这些相关寄存器中的页表项信息写入TLB中的指定项。其中,如果CSR.TLBIND.NE置为1,写入的是一个无效TLB项。
- tlbfill:类似于tlbwr指令,不同的是tlbfill写入TLB位置由硬件随机决定。
具体案例可见后文相关代码分析。
(2)页表遍历相关指令
- lddir rd, rj, level:访问页目录项。
- level表示访问页表的级别。参考上一篇文章中的页表分级。level为1-4分别对应CSR.PWCL中的PT、Dir1、Dir2、Dir3。
- 如果通用寄存器rj中第6位是0,则rj表示第level级页表的基址。此时lddir指令会根据当前处理的TLB重填地址访问level级页表,取回对应level-1级页表的基址到rd中。
- 如果通用寄存器rj中第6位是1,则rj为一个大页的页表项。此时lddir指令会直接将rj写入到rd中。
- ldpte rj, req:访问页表项。
- seq表示访问的是奇数页还是偶数页。访问偶数页时结果将写入CSR.TLBRELO0,访问奇数页时结果将写入CSR.TLBRELO1。
- 如果通用寄存器rj中第6位是0,则rj表示末级页表的基址。此时ldpte指令会根据当前处理的TLB重填地址访问末级页表,取回对应页表项到CSR.TLBRELO0或CSR.TLBRELO1中。
- 如果通用寄存器rj中第6位是1,则rj为一个大页的页表项。此时lddir指令会直接将rj转换为最终的页表项格式写入到CSR.TLBRELO0或CSR.TLBRELO1中。
具体案例可见后文相关代码分析。
5、TLB相关异常处理
下面结合linux源码对TLB相关异常处理进行分析。
linux中TLB相关异常和相关处理函数的对应关系如下:
- TLB重填异常:handle_tlb_refill
- load/取指操作页无效异常:handle_tlb_load
- store操作页无效异常:handle_tlb_store
- 页修改异常:handle_tlb_modify
- 页不可读/不可写/特权不合规异常:handle_tlb_protect
这里分析handle_tlb_refill、handle_tlb_load和handle_tlb_protect函数。其中handle_tlb_store和handle_tlb_modify实际上流程与handle_tlb_load基本一致,只是更新页表项时更新的位不同。
(1)TLB重填异常
TLB重填异常(handle_tlb_refill)触发前后硬件中的处理与一般异常存在差异,主要是TLB重填异常相关有独立的一套寄存器。但都会有相应保存和恢复现场、跳转和返回操作。值得注意的是,TLB重填异常中出错的地址保存在CSR.TLBRBADV寄存器,而一般异常出错的地址保存在CSR.BADV寄存器。
TLB重填异常的软件处理过程如下:
- 保存现场
- 根据CSR.TLBRBADV中记录的缺失虚拟地址,和CSR.PGD中pgd基址,遍历发生TLB重填异常的进程的多级页表,从内存中取回页表项信息并填入CSR.TLBELO0和CSR.TLBELO1寄存器的相应域中
- 根据填入的CSR.TLBELO0和CSR.TLBELO1寄存器信息,最终用tlbfill指令将页表项填入TLB
- 恢复并返回
代码分析如下:
(2)load/取指操作页无效异常
load/取指操作页无效异常触发前后硬件中的处理与一般异常相同。
handle_tlb_load处理的过程如下:
- 保存现场
- 根据CSR.BADV中记录的缺失虚拟地址,和CSR.PGD中pgd基址,遍历发生异常的进程的多级页表,从内存中取回页表项(或大页)信息
- 判断该页表项是否存在,如果不存在则会跳转执行缺页处理函数
- 如果存在则将页表项置为有效并填入TLB。最后恢复并返回
代码分析如下:
(3)页不可读/不可写/特权不合规异常
页不可读/不可写/特权不合规异常触发前后硬件中的处理与一般异常相同。
handle_tlb_protect处理的过程实际上就是调用缺页处理函数,来填入页表。
代码分析如下:
总结
本文介绍了loongarch架构中软件管理TLB的机制、TLB重填异常和其他TLB相关的异常,以及相应的异常处理和代码分析。
软件管理TLB机制、处理TLB相关异常,算是loongarch架构中TLB相关软件维护中较为特别的地方。下一篇文章将继续介绍loongarch中其他的TLB维护和相关指令。