当前位置: 首页 > 科技观察

Loongarch架构介绍(四)TLB异常处理

时间:2023-03-12 19:58:17 科技观察

了解更多开源请访问:开源基础软件社区https://ost.51cto.comloongarch架构,涉及到虚拟内存系统中页表的管理。本文介绍TLB相关的异常处理,并结合代码进行分析。由于loongarch架构采用了软件管理TLB的方式,其处理流程和软件所需的管理操作与很多常见的架构不同。1.TLB表项和页表项首先介绍一下TLB表项和页表项的格式,以便基本了解。(1)页表项格式下图为loonarch中的页表项格式:下面为大家做说明:V:有效位。标志页表条目有效。D:脏位。标志页内容需要写回。MAT:存储访问类型。见上一篇文章。G:是否为全局页面。P:物理页是否存在。W:可写与否。H:是否是大页面。PA:物理地址的高地址部分。NR:不可读位。表示页面不可读。NX:不可执行位。表示该页面不可执行。PLV和RPLV:特权级,当RPLV=0时,任何特权级不低于PLV的程序都可以访问;当RPLV=1时,只能被特权级等于PLV的程序访问,大页的页表项页表项与基本页格式的主要区别在于H位和G位。而基本页的页表项在最后一级页表中,大页的页表项实际上替代了原来的页目录项。另外,对于基本页的页表项,loonarch中的每一个页表项都存储了一对相邻的校验和相邻的页表信息。如下图所示:(2)TLBentryformat下图为loonarch中的TLBentry格式:每个TLBentry分为两部分:第一行是比较部分,后面两行是物理转换部分。TLB表项的比较部分包括:E:存在位,1位。当它为1时,表示存在。用于查找TLB时的判断。ASID:地址空间标识符,10位。用于区分不同进程或虚拟地址空间,减少进程上下文切换等操作时刷新TLB带来的性能损失。ASID也必须在TLB查找期间匹配。有关详细信息,请参阅以下文章。G:全局位,1位。用于取消ASID的功能,标识为全局入口。PS:页面大小,6位。用于支持不同的页面大小。例如,对于16KB的页面,PS=14。VPPN:虚拟双页号,对应一对页表项信息。对于基本页的页表项,loongarch中的每一个页表项都存储了一对相邻的奇偶校验和相邻的页表信息,所以TLB表项中存储的虚拟页号是虚拟页号/2中的内容系统,即虚拟页号的最低位不需要放在TLB中。在查找TLB时,根据要查找的虚拟页号的最低位来决定选择奇数页还是偶数页的物理转换信息。对于大页的页表项,硬件会自动将其分成两个大小减半的页表项存储在TLB中。例如,对于32MB的大页面,TLB将存储两个大小为16MB的条目。由于TLB和页表双页存储的特点,每个TLB表项中有两个物理翻译信息。物理转换信息中的PPN为物理页码,其他对应上面页表中的。2、软件管理TLB与MIPS架构类似,loonarch采用了软件管理TLB的方式。在大多数其他体系结构中,TLB由硬件管理。软件管理的TLB带来了更大的灵活性,但性能相对较差。在硬件管理TLB中,当忽略pagefault和cache的细节时,虚拟地址转换为物理地址的过程如下:其中,TLBmiss后查找页表的过程为由硬件自动完成,软件只需要处理后面产生的pagefault。整个过程中最多出现一次缺页错误。下图为软件管理TLB方案中虚拟地址转换为物理地址的过程:具体解释如下:TLBmiss后,会出现第一次TLB重填(TLBrefill)异常,软件需要遍历TLBrefillexceptionTable中的page和fillTLBentries,这里应该是软件管理TLB和硬件管理TLB最大的区别。然后在TLBrefill异常处理中,如果填充的页表项仍然无效,那么返回后再次查询TLB时,会产生其他错误处理,进行第二次填充页表项,或者填充页表条目和TLB条目。如果在第二次故障处理过程中没有重新填充TLB表项,返回后再次查询TLB时会产??生第三次TLB重新填充异常。可以看出,TLBmiss后查找页表的过程需要软件来处理,整个过程最多可以出现3次异常。另外,硬件会保证在TLBrefillexception期间不会再次产生TLBrefillexception。3、TLB相关异常loongarch中TLB相关异常包括:TLBrefillexception:该异常发生在TLBmiss之后。加载操作页面无效异常:加载操作的虚拟地址在TLB中找到匹配,但是匹配的V=值为0时,触发存储操作无效页面异常:存储的虚拟地址操作在TLB中找到,但是当匹配的V为0时,触发取操作。page无效异常:fetch操作的虚拟地址在TLB匹配项中找到,但是当匹配项的V=0时,触发pageprivilegelevelviolation异常:内存访问操作的虚拟地址找到匹配项项在TLB和V=1,但访问权限级别不符合要求。请参考上面页表条目中的PLV和RPLV字段。页面修改异常:store操作的虚拟地址在TLB中找到匹配且V=1且权限符合,但是当匹配的D=0时,触发页面不可读异常:load操作的虚拟地址为在TLB中找到match且V=1且权限符合,但是当match的NR=1时页面无法执行触发异常:取指令操作的虚拟地址在TLB中找到匹配且V=1且权限合规,但匹配项的NX=1时触发。当TLBrefill异常时,需要遍历页表进行refill。TLB重填异常不同于一般异常。它有一个独立的异常入口,一个独立的用于维护场景的控制状态寄存器,一组独立的TLB访问接口控制寄存器。因此,TLBrefill异常可以用于其他异常处理流程。被触发。当进入TLBrefill异常时,硬件会自动设置CSR.CRMD.DA=1和CSR.CRMD.PG=0,即进入直接地址转换模式,避免TLBrefill在TLBrefill异常中发生。异常。对于加载操作中的无效异常等异常,需要完成类似页面错误的工作。4.相关指令在介绍TLB相关异常的处理之前,先介绍一下loonarch中的相关指令。(1)TLB异常处理相关指令tlbsrch:查询TLB对应索引。该指令使用CSR.ASID和CSR.TLBEHI中的信息来查询TLB。如果命中,则将其对应的索引写入CSR.TLBIDX.Index,否则将CSR.TLBIDX.NE置1。在tlbwr等指令中可以使用index来表示该操作的TLB索引。tlbrd:读取索引对应的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)页表遍历相关指令lddirrd,rj,level:访问页目录项。level表示访问页表的级别。参考上一篇文章中的PageTableHierarchy。级别1-4分别对应于CSR.PWCL中的PT、Dir1、Dir2和Dir3。如果通用寄存器rj的第6位为0,那么rj代表的是level级页表的基地址。此时lddir命令会根据当前处理的TLBrefill地址访问level级页表,并将对应的level-1级页表的基地址检索到rd中。如果通用寄存器rj的第6位为1,则rj为大页的页表项。这时lddir命令会直接将rj写入rd。ldpterj,req:访问页表条目。seq表示访问的是奇数页还是偶数页。对于偶数页访问,结果写入CSR.TLBRELO0,对于奇数页访问,结果写入CSR.TLBRELO1。如果通用寄存器rj的第6位为0,则rj表示最后一级页表的基地址。此时ldpte指令会根据当前处理的TLBrefill地址访问末级页表,将对应的页表项取回CSR.TLBRELO0或CSR.TLBRELO1。如果通用寄存器rj的第6位为1,则rj为大页的页表项。此时lddir指令会直接将rj转换成最终的页表项格式写入CSR.TLBRELO0或CSR.TLBRELO1中。具体案例见下文相关代码分析。5、TLB相关的异常处理下面结合linux源码分析TLB相关的异常处理。TLB相关异常与Linux中相关处理函数的对应关系如下:TLBrefill异常:handle_tlb_refillload/fetch操作页无效异常:handle_tlb_loadstore操作页无效异常: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)TLBrefillexceptionTLBrefill异常(handle_tlb_refill)触发前后硬件中的处理与一般异常有所不同。主要原因是有一组独立的寄存器与TLBrefill异常相关。但是会有相应的保存和还原场景,跳转和返回操作。值得注意的是,TLBrefill异常中错误的地址存放在CSR.TLBRBADV寄存器中,一般异常错误的地址存放在CSR.BADV寄存器中。TLBrefill异常的软件处理流程如下:保存场景根据CSR.TLBRBADV中记录的缺失虚拟地址和CSR.PGD中的pgd基地址,遍历TLBrefill所在进程的多级页表发生异常,从内存中取出。返回页表项信息,填入CSR.TLBELO0和CSR.TLBELO1寄存器的相应字段。根据填充的CSR.TLBELO0和CSR.TLBELO1寄存器信息,最后使用tlbfill指令将页表项填充到TLB中进行恢复,返回代码分析如下:SYM_FUNC_START(handle_tlb_refill)csrwrt0,LOONGARCH_CSR_TLBRSAVE//将t0保存到CSR.TLBRSAVEregistercsrrdt0,LOONGARCH_CSR_PGD//读取pgd基地址到t0lddirt0,t0,3//根据CSR.TLBRBADVAddress中缺失的虚记录,//访问level3页表,读取的基地址2级页表到t0(pgd是3级页表的基地址)#ifCONFIG_PGTABLE_LEVELS>3lddirt0,t0,2//根据CSR.TLBRBADV中的记录缺少虚拟地址,//访问级别2页表,读取一级页表基地址到t0#endif#ifCONFIG_PGTABLE_LEVELS>2lddirt0,t0,1//根据CSR.TLBRBADV中记录的缺失虚拟地址,//访问一级页表,读取最后一级页表地址或大页到t0#endifldptet0,0//根据CSR.TLBRBADV中记录的缺失虚拟地址,//访问最后一级页表或大页,读取偶数页表项或大页到CSR.TLBELO0ldptet0,1//根据CSR.TLBRBADV中记录的缺失虚拟地址,//访问最后一级页表或大页,读取奇数页表项或大页ToCSR.TLBELO1tlbfill//根据CSR中的信息.TLBELO0、CSR.TLBELO1等寄存器,//将页表项填入TLBcsrrdt0,LOONGARCH_CSR_TLBRSAVE//恢复t0ertn//异常返回SYM_FUNC_END(handle_tlb_refill)handle_tlb_load的过程和一般异常一样:根据记录的缺失虚拟地址保存场景CSR.BADV和CSR.PGD中的pgd基地址,遍历异常发生进程的多级页表,从内存中获取页表项(或大页)信息,判断页表项是否为存在。如果不存在,则跳转到执行缺页处理函数。如果存在,则页表项将有效并填充到TLB中。最后的恢复和返回码分析如下:SYM_FUNC_START(handle_tlb_load)//将t0,t1,ra写入CSR.SAVE0-CSR.SAVE3,scratchregistercsrwrt0,EXCEPTION_KS0csrwrt1,EXCEPTION_KS1csrwrra,EXCEPTION_KS2/**vmalloc处理不在热路径中。*///如果CSR.BADV不小于0,继续vmalloc_done_load//将CSR.BADV和CSR.PGDL读入t0和t1//否则跳转到vmalloc_load转换CSR.BADV用swapper_pg_dir读取t0和t1//即CSR.BADV不小于0时,使用内核地址下半部分的pgd,//否则使用用户地址上半部分的pgdcsrrdt0,LOONGARCH_CSR_BADVbltzt0,vmalloc_loadcsrrdt1,LOONGARCH_CSR_PGDLvmalloc_done_load:/*以字节为单位获取PGD偏移量*///根据t0中的CSR.BADV地址和t1中的pgd基地址,遍历页表找到bstrpick.dra,t0,PTRS_PER_PGD_BITS+PGDIR_SHIFT-1,PGD??IR_SHIFTalsl.dt1,ra,t1,3#ifCONFIG_PGTABLE_LEVELS>3ld.dt1,t1,0bstrpick.dra,t0,PTRS_PER_PUD_BITS+PUD_SHIFT-1,PUD_SHIFTalsl.dt1,ra,t1,3#endif#ifCONFIG_PGTABLE_LEVELS>2ld.dt1,t1,0bstrpick.dra,t0,PTRS_PER_PMD_BITS+PMD_SHIFT-1,PMD_SHIFTalsl.dt1,ra,t1,3//这里t1是1级页表(pmd)地址#endif//put1Rald.dra,t1,0/**对于巨大的tlb条目,pmde不包含地址但*而是包含tlbpte。检查PAGE_HUGE位并*看看我们是否需要跳转到巨大的tlb处理。*///如果ra中的条目是大页,则跳转到tlb_huge_update_loadrotri.dra,ra,_PAGE_HUGE_SHIFT+1bltzra,tlb_huge_update_loadrotri.dra,ra,64-(_PAGE_HUGE_SHIFT+1)bstrpick.dt0,t0,PTRS_PER_PTE_BITS+PAGE_SHIFT-1,PAGE_SHIFTalsl.dt1,t0,ra,_PTE_T_LOG2//这里t1是CSR.BADV对应的最后一级页表入口地址//读取页表入口到t0#ifdefCONFIG_SMPsmp_pgtable_change_load:ll.dt0,t1,0//smp使用ll/sc原子指令写循环#elseld.dt0,t1,0#endif//如果页表表项不存在,则跳转到nopage_tlb_load调用缺页处理函数//否则继续向下执行,将相应的有效页表项写入TLBandira,t0,_PAGE_PRESENTbeqzra,nopage_tlb_load//设置有效位并更新页表项orit0,t0,_PAGE_VALID#ifdefCONFIG_SMPsc.dt0,t1,0beqzt0,smp_pgtable_change_load//写入失败时跳转#elsest.dt0,t1,0#endif//根据CSR.ASID和CSR.TLBEHI的信息查询TLB,这样就可以写入tlbwr指令//如果命中,则将其索引写入CSR.TLBIDX,否则设置CSR。TLBIDX.NEto1//这里肯定会命中tlbsrch//t0=evenpagetableentry,t1=oddpagetableentrybstrins.dt1,zero,3,3ld.dt0,t1,0ld.dt1,t1,8//写入TLB相关寄存器csrwrt0,LOONGARCH_CSR_TLBELO0csrwrt1,LOONGARCH_CSR_TLBELO1//根据CSR.TLBELO0,CSR.TLBELO1,CSR.TBLIDX等相关寄存器信息,//将页表项信息写入TLBCSR.TBLIDX.index对应位置tlbwr//恢复并返回csrrdt0,EXCEPTION_KS0csrrdt1,EXCEPTION_KS1csrrdra,EXCEPTION_KS2ertn#ifdefCONFIG_64BITvmalloc_load:la.abst1,swapper_pg_dirbvmalloc_done_load#endif/*这是entry_pg_dirbvmalloc_done_load#endif/*这是entry_huupate_phuupate点a://对于大页,异常处理过程和上面的基本页表项处理过程基本相同//只是在填充TLB时会做一些额外的格式转换处理,这里不再赘述。。。nopage_tlb_load:dbar0csrrdra,EXCEPTION_KS2la.abst0,tlb_do_page_fault_0jrt0SYM_FUNC_END(handle_tlb_load)(3)pageunreadable/unwritable/privilegednon-compliant异常处理前后触发pageunreadable/unwritable/privilegednon-compliant异常硬件上和一般异常一样handle_tlb_protect处理的过程其实就是调用pagefault处理函数填充页表。代码分析如下:SYM_FUNC_START(handle_tlb_protect)//保存寄存器BACKUP_T0T1SAVE_ALL//设置参数transfermovea0,spmovea1,zerocsrrda2,LOONGARCH_CSR_BADVREG_Sa2,sp,PT_BVADDR//calldo_page_faultla.absfa,jirl_t0,0//恢复并返回RESTORE_ALL_AND_RETSYM_FUNC_END(handle_tlb_protect)总结本文介绍了loongarch架构中软件管理TLB的机制,TLB重填异常和其他TLB相关的异常,以及相应的异常处理和代码分析。软件管理TLB机制和TLB相关异常的处理是loonarch架构中TLB相关软件维护中比较特殊的地方。下一篇文章将继续介绍loonarch中其他TLB的维护及相关说明。了解更多开源请访问:开源基础软件社区https://ost.51cto.com